dsp: firmware_update: Introduce iterators for parsing firmware packages

The new collection of APIs allows parsing of arbitrary PLDM firmware
update packages in a style that is reasonably ergonomic and free of
library allocations.

As a rough sketch, use of the API looks as follows:

```
/* Pin the package parsing to format revision 2 */
DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);

struct pldm_package_downstream_device_id_record ddrec;
struct pldm_package_component_image_information info;
struct pldm_package_firmware_device_id_record fdrec;
pldm_package_header_information_pad hdr;
struct pldm_package_iter iter;
int rc;

...

rc = decode_pldm_firmware_update_package(package, in, &pin, &hdr, &iter);
if (rc < 0) { ... }

/* Do something with hdr */

foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc)
{
	struct pldm_descriptor desc;

	/* Do something with fdrec */

	foreach_pldm_package_firmware_device_id_record_descriptor(iter, fdrec,
								  desc, rc)
	{
		/* Do something with desc */
	}
	if (rc) { /* Handle desc decode failure */ }
}
if (rc) { /* Handle fdrec decode failure */ }

foreach_pldm_package_downstream_device_id_record(iter, ddrec, rc)
{
	struct pldm_descriptor desc;

	/* Do something with ddrec */

	foreach_pldm_package_downstream_device_id_record_descriptor(iter, ddrec,
								    desc, rc)
	{
		/* Do something with desc */
	}
	if (rc) { /* Handle desc decode failure */ }
}
if (rc) { /* Handle ddrec decode failure */ }

foreach_pldm_package_component_image_information(iter, info, rc)
{
	/* Do something with info */
}
if (rc) { /* Handle info decode failure */ }
```

Note that these new APIs intersects with some existing functionality.
Where possible, implementations have been unified, however, the existing
APIs were inflexible and unergonomic, requiring pointer arithmetic on
the part of the caller, and could not safely cater to concepts added in
later revisions of the spec. The existing APIs will be deprecated in a
later patch.

Further to that, the implementation of the component image information
parsing could not be unified in a way that wasn't awkward. The existing
API passed raw image offsets back to the caller, where the new API
resolves the offsets against the image location in memory, yielding a
valid pointer and bounds-checked length (thereby relieving the caller of
the pointer arithmetic). Unwinding that back to a raw offset seems
tedious at best in the context of other tests for pointer validity, so
don't even try.

Change-Id: I53472ca22b4c8aa79a5515b20a72bf8f66ed66e3
Co-developed-by: Rajeev Ranjan <rajeeranjan@nvidia.com>
Signed-off-by: Andrew Jeffery <andrew@codeconstruct.com.au>
diff --git a/CHANGELOG.md b/CHANGELOG.md
index c43c4d3..270a4f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -35,6 +35,7 @@
 
 - firmware update: Add encode/decode API for downstream device update command
 - base: Add `PLDM_ERROR_UNEXPECTED_TRANSFER_FLAG_OPERATION` completion code
+- Introduce interator-based firmware update package parsing APIs
 
 ### Changed
 
diff --git a/include/libpldm/compiler.h b/include/libpldm/compiler.h
index c70bcab..cf9ab50 100644
--- a/include/libpldm/compiler.h
+++ b/include/libpldm/compiler.h
@@ -3,6 +3,9 @@
 #ifndef LIBPLDM_COMPILER_H
 #define LIBPLDM_COMPILER_H
 
+#define LIBPLDM_SIZEAT(type, member)                                           \
+	(offsetof(type, member) + sizeof(((type *)NULL)->member))
+
 #if defined __has_attribute
 
 #if __has_attribute(always_inline)
diff --git a/include/libpldm/firmware_update.h b/include/libpldm/firmware_update.h
index 3571b9b..cdf4c6b 100644
--- a/include/libpldm/firmware_update.h
+++ b/include/libpldm/firmware_update.h
@@ -6,11 +6,14 @@
 extern "C" {
 #endif
 
+#include "compiler.h"
+
 #include <libpldm/api.h>
 #include <libpldm/base.h>
 #include <libpldm/pldm_types.h>
 #include <libpldm/utils.h>
 
+#include <assert.h>
 #include <stdbool.h>
 #include <stddef.h>
 #include <stdint.h>
@@ -2160,6 +2163,728 @@
 			      const struct pldm_cancel_update_resp *resp_data,
 			      struct pldm_msg *msg, size_t *payload_length);
 
+/** @brief Firmware update v1.0 package header identifier */
+#define PLDM_PACKAGE_HEADER_IDENTIFIER_V1_0                                    \
+	{ 0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43,                      \
+	  0x98, 0x00, 0xA0, 0x2F, 0x05, 0x9A, 0xCA, 0x02 }
+
+/** @brief Firmware update v1.0 package header format revision */
+#define PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR01H 0x01
+
+/** @brief Firmware update v1.1 package header identifier */
+#define PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1                                    \
+	{                                                                      \
+		0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18,                \
+		0xa0, 0x30, 0xfc, 0x8a, 0x56, 0x58, 0x7d, 0x5a,                \
+	}
+
+/** @brief Firmware update v1.1 package header format revision */
+#define PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H 0x02
+
+/** @brief Client-side version pinning for package format parsing
+ *
+ * Parsing a firmware update package requires the package to be of a revision
+ * defined in the specification, for libpldm to support parsing a package
+ * formatted at the specified revision, and for the client to support calling
+ * libpldm's package-parsing APIs in the manner required for the package format.
+ *
+ * pldm_package_format_pin communicates to libpldm the maximum package format
+ * revision supported by the client.
+ *
+ * The definition of the pldm_package_format_pin object in the client
+ * application should not be open-coded. Instead, users should call on of the
+ * following macros:
+ *
+ * - @ref DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR01H
+ * - @ref DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H
+ */
+struct pldm_package_format_pin {
+	struct {
+		/** A value that communicates information about object sizes to the implementation */
+		const uint32_t magic;
+
+		/** Versioning for the derivation of the magic value */
+		const uint8_t version;
+	} meta;
+	struct {
+		/** The maximum supported package format UUID */
+		const pldm_uuid identifier;
+
+		/** The maximum supported header format revision */
+		const uint8_t revision;
+	} format;
+};
+
+/**
+ * @brief Header information as parsed from the provided package
+ *
+ * See Table 3, DSP0267 v1.3.0.
+ *
+ * The provided package data must out-live the header struct.
+ */
+struct pldm__package_header_information {
+	pldm_uuid package_header_identifier;
+	uint8_t package_header_format_revision;
+	uint8_t package_release_date_time[PLDM_TIMESTAMP104_SIZE];
+	uint16_t component_bitmap_bit_length;
+	uint8_t package_version_string_type;
+
+	/** A field pointing to the package version string in the provided package data */
+	struct variable_field package_version_string;
+
+	/* TODO: some metadata for the parsing process is stored here, reconsider */
+	struct variable_field areas;
+	struct variable_field package;
+};
+/* TODO: Deprecate the other struct pldm_package_header_information, remove, drop typedef */
+typedef struct pldm__package_header_information
+	pldm_package_header_information_pad;
+
+/* TODO: Consider providing an API to access valid bits */
+struct pldm_package_component_bitmap {
+	struct variable_field bitmap;
+};
+
+/**
+ * @brief A firmware device ID record from the firmware update package
+ *
+ * See Table 4, DSP0267 v1.3.0.
+ *
+ * The provided package data must out-live the @ref "struct
+ * pldm_package_firmware_device_id_record" instance.
+ */
+struct pldm_package_firmware_device_id_record {
+	uint8_t descriptor_count;
+	bitfield32_t device_update_option_flags;
+	uint8_t component_image_set_version_string_type;
+
+	/**
+	 * A field pointing to the component image set version string in the provided
+	 * package data.
+	 */
+	struct variable_field component_image_set_version_string;
+
+	/**
+	 * A field pointing to the to a bitmap of length @ref
+	 * component_bitmap_bit_length in the provided package data..
+	 */
+	struct pldm_package_component_bitmap applicable_components;
+
+	/**
+	 * A field pointing to record descriptors for the
+	 * firmware device. Iterate over the entries using @ref
+	 * foreach_pldm_package_firmware_device_id_record_descriptor
+	 *
+	 * See Table 7, DSP0267 v1.3.0
+	 */
+	struct variable_field record_descriptors;
+	struct variable_field firmware_device_package_data;
+};
+
+/**
+ * @brief A downstream device ID record from the firmware update package
+ *
+ * See Table 5, DSP0267 v1.3.0.
+ *
+ * The provided package data must out-live the @ref "struct
+ * pldm_package_downstream_device_id_record" instance.
+ */
+struct pldm_package_downstream_device_id_record {
+	uint8_t descriptor_count;
+	bitfield32_t update_option_flags;
+	uint8_t self_contained_activation_min_version_string_type;
+
+	/**
+	 * A field pointing to the self-contained activation minimum version string in
+	 * the provided package data.
+	 */
+	struct variable_field self_contained_activation_min_version_string;
+	uint32_t self_contained_activation_min_version_comparison_stamp;
+
+	/**
+	 * A field pointing to a bitmap of length @ref component_bitmap_bit_length in
+	 * the provided package data.
+	 */
+	struct pldm_package_component_bitmap applicable_components;
+
+	/**
+	 * A field pointing to record descriptors for the
+	 * downstream device. Iterate over the entries using @ref
+	 * foreach_pldm_package_downstream_device_id_record_descriptor
+	 *
+	 * See Table 7, DSP0267 v1.3.0
+	 */
+	struct variable_field record_descriptors;
+
+	/**
+	 * A field that may point to package data to be proxied by the firmware device.
+	 * If present, points into the provided package data.
+	 */
+	struct variable_field package_data;
+};
+
+/**
+ * @brief Component image information from the firmware update package.
+ *
+ * See Table 6, DSP0267 v1.3.0
+ *
+ * The provided package data must out-live the @ref "struct
+ * pldm_package_component_image_information" instance.
+ */
+struct pldm_package_component_image_information {
+	uint16_t component_classification;
+	uint16_t component_identifier;
+	uint32_t component_comparison_stamp;
+	bitfield16_t component_options;
+	bitfield16_t requested_component_activation_method;
+
+	/**
+	 * A field that points to the component image for a device in the provided
+	 * package data.
+	 */
+	struct variable_field component_image;
+	uint8_t component_version_string_type;
+
+	/**
+	 * A field that points to the component version string for the image in the
+	 * provided package data.
+	 */
+	struct variable_field component_version_string;
+};
+
+struct pldm_package_firmware_device_id_record_iter {
+	struct variable_field field;
+	size_t entries;
+};
+
+struct pldm_package_downstream_device_id_record_iter {
+	struct variable_field field;
+	size_t entries;
+};
+
+struct pldm_package_component_image_information_iter {
+	struct variable_field field;
+	size_t entries;
+};
+
+/**
+ * @brief State tracking for firmware update package iteration
+ *
+ * Declare an instance on the stack to be initialised by @ref
+ * decode_pldm_firmware_update_package
+ *
+ * The state is consumed by the following macros:
+ *
+ * - @ref foreach_pldm_package_firmware_device_id_record
+ * - @ref foreach_pldm_package_firmware_device_id_record_descriptor
+ * - @ref foreach_pldm_package_downstream_device_id_record
+ * - @ref foreach_pldm_package_downstream_device_id_record_descriptor
+ * - @ref foreach_pldm_package_component_image_information
+ */
+struct pldm_package_iter {
+	const pldm_package_header_information_pad *hdr;
+
+	/* Modified in the course of iteration */
+	struct pldm_package_firmware_device_id_record_iter fds;
+	struct pldm_package_downstream_device_id_record_iter dds;
+	struct pldm_package_component_image_information_iter infos;
+};
+
+/**
+ * @brief Initialize the firmware update package iterator.
+ *
+ * @param[in] data The buffer containing the complete firmware update package
+ * @param[in] length The length of the buffer pointed at by @p data
+ * @param[in] pin The maximum supported package format revision of the caller
+ * @param[out] hdr The parsed package header structure
+ * @param[out] iter State-tracking for parsing subsequent package records and components
+ *
+ * Must be called to ensure version requirements for parsing are met by all
+ * components, and to initialise @p iter prior to any subsequent extraction of
+ * package records and components.
+ *
+ * @note @p data is stored in @iter for later reference, and therefore must
+ *       out-live @p iter
+ * @note @p hdr is stored in @iter for later reference, and therefore must
+ *       out-live @p iter
+ *
+ * @return 0 on success. Otherwise, a negative errno value:
+ * - -EBADMSG if the package fails to meet minimum required length for a valid
+ *   package
+ * - -EINVAL if provided parameter values are invalid
+ * - -ENOTSUP on unrecognised or unsupported versions for the format pin or
+ *   provided package
+ * - -EOVERFLOW if the variable length structures extend beyond the package
+ *   data buffer
+ * - -EPROTO if parsed values violate the package format specification
+ * - -EUCLEAN if the package fails embedded integrity checks
+ */
+int decode_pldm_firmware_update_package(
+	const void *data, size_t length,
+	const struct pldm_package_format_pin *pin,
+	pldm_package_header_information_pad *hdr,
+	struct pldm_package_iter *iter);
+
+LIBPLDM_ITERATOR
+bool pldm_package_firmware_device_id_record_iter_end(
+	const struct pldm_package_firmware_device_id_record_iter *iter)
+{
+	return iter->entries == 0;
+}
+
+LIBPLDM_ITERATOR
+bool pldm_package_firmware_device_id_record_iter_next(
+	struct pldm_package_firmware_device_id_record_iter *iter)
+{
+	if (!iter->entries) {
+		return false;
+	}
+	iter->entries--;
+	return true;
+}
+
+int pldm_package_firmware_device_id_record_iter_init(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_firmware_device_id_record_iter *iter);
+
+int decode_pldm_package_firmware_device_id_record_from_iter(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_firmware_device_id_record_iter *iter,
+	struct pldm_package_firmware_device_id_record *rec);
+
+/**
+ * @brief Iterate over a package's firmware device ID records
+ *
+ * @param iter[in,out] The lvalue for the instance of @ref "struct pldm_package_iter"
+ *             initialised by @ref decode_pldm_firmware_update_package
+ * @param rec[out] An lvalue of type @ref "struct pldm_package_firmware_device_id_record"
+ * @param rc[out] An lvalue of type int that holds the status result of parsing the
+ *                firmware device ID record
+ *
+ * @p rc is set to 0 on successful decode. Otherwise, on error, @p rc is set to:
+ * - -EINVAL if parameters values are invalid
+ * - -EOVERFLOW if the package layout exceeds the bounds of the package buffer
+ * - -EPROTO if package metadata doesn't conform to specification constraints
+ *
+ * Example use of the macro is as follows:
+ *
+ * @code
+ * DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+ *
+ * struct pldm_package_firmware_device_id_record fdrec;
+ * pldm_package_header_information_pad hdr;
+ * struct pldm_package_iter iter;
+ * int rc;
+ *
+ * rc = decode_pldm_firmware_update_package(package, in, &pin, &hdr,
+ * 					 &iter);
+ * if (rc < 0) {
+ * 	   // Handle header parsing failure
+ * }
+ * foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc) {
+ * 	   // Do something with fdrec
+ * }
+ * if (rc) {
+ * 	   // Handle parsing failure for fdrec
+ * }
+ * @endcode
+ */
+#define foreach_pldm_package_firmware_device_id_record(iter, rec, rc)          \
+	for ((rc) = pldm_package_firmware_device_id_record_iter_init(          \
+		     (iter).hdr, &(iter).fds);                                 \
+	     !(rc) &&                                                          \
+	     !pldm_package_firmware_device_id_record_iter_end(&(iter).fds) &&  \
+	     !((rc) = decode_pldm_package_firmware_device_id_record_from_iter( \
+		       (iter).hdr, &(iter).fds, &(rec)));                      \
+	     pldm_package_firmware_device_id_record_iter_next(&(iter).fds))
+
+LIBPLDM_ITERATOR
+struct pldm_descriptor_iter
+pldm_package_firmware_device_id_record_descriptor_iter_init(
+	struct pldm_package_firmware_device_id_record_iter *iter,
+	struct pldm_package_firmware_device_id_record *rec)
+{
+	(void)iter;
+	return (struct pldm_descriptor_iter){ &rec->record_descriptors,
+					      rec->descriptor_count };
+}
+
+/**
+ * @brief Iterate over the descriptors in a package's firmware device ID record
+ *
+ * @param iter[in,out] The lvalue for the instance of @ref "struct pldm_package_iter"
+ *             initialised by @ref decode_pldm_firmware_update_package
+ * @param rec[in] An lvalue of type @ref "struct pldm_package_firmware_device_id_record"
+ * @param desc[out] An lvalue of type @ref "struct pldm_descriptor" that holds
+ *                  the parsed descriptor
+ * @param rc[out] An lvalue of type int that holds the status result of parsing the
+ *                firmware device ID record
+ *
+ * @p rc is set to 0 on successful decode. Otherwise, on error, @p rc is set to:
+ * - -EINVAL if parameters values are invalid
+ * - -EOVERFLOW if the package layout exceeds the bounds of the package buffer
+ * - -EPROTO if package metadata doesn't conform to specification constraints
+ *
+ * Example use of the macro is as follows:
+ *
+ * @code
+ * DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+ *
+ * struct pldm_package_firmware_device_id_record fdrec;
+ * pldm_package_header_information_pad hdr;
+ * struct pldm_package_iter iter;
+ * int rc;
+ *
+ * rc = decode_pldm_firmware_update_package(package, in, &pin, &hdr,
+ * 					 &iter);
+ * if (rc < 0) { ... }
+ *
+ * foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc) {
+ *     struct pldm_descriptor desc;
+ *
+ * 	   ...
+ *
+ *     foreach_pldm_package_firmware_device_id_record_descriptor(
+ *             iter, fdrec, desc, rc) {
+ *         // Do something with desc
+ *     }
+ *     if (rc) {
+ *         // Handle failure to parse desc
+ *     }
+ * }
+ * if (rc) { ... }
+ * @endcode
+ */
+#define foreach_pldm_package_firmware_device_id_record_descriptor(iter, rec,      \
+								  desc, rc)       \
+	for (struct pldm_descriptor_iter desc##_iter =                            \
+		     ((rc) = 0,                                                   \
+		     pldm_package_firmware_device_id_record_descriptor_iter_init( \
+			      &(iter).fds, &(rec)));                              \
+	     (!pldm_descriptor_iter_end(&(desc##_iter))) &&                       \
+	     !((rc) = decode_pldm_descriptor_from_iter(&(desc##_iter),            \
+						       &(desc)));                 \
+	     pldm_descriptor_iter_next(&(desc##_iter)))
+
+LIBPLDM_ITERATOR
+bool pldm_package_downstream_device_id_record_iter_end(
+	const struct pldm_package_downstream_device_id_record_iter *iter)
+{
+	return iter->entries == 0;
+}
+
+LIBPLDM_ITERATOR
+bool pldm_package_downstream_device_id_record_iter_next(
+	struct pldm_package_downstream_device_id_record_iter *iter)
+{
+	if (!iter->entries) {
+		return false;
+	}
+	iter->entries--;
+	return true;
+}
+
+int pldm_package_downstream_device_id_record_iter_init(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_firmware_device_id_record_iter *fds,
+	struct pldm_package_downstream_device_id_record_iter *dds);
+
+int decode_pldm_package_downstream_device_id_record_from_iter(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_downstream_device_id_record_iter *iter,
+	struct pldm_package_downstream_device_id_record *rec);
+
+/**
+ * @brief Iterate over a package's downstream device ID records
+ *
+ * @param iter[in,out] The lvalue for the instance of @ref "struct pldm_package_iter"
+ *             initialised by @ref decode_pldm_firmware_update_package
+ * @param rec[out] An lvalue of type @ref "struct pldm_package_downstream_device_id_record"
+ * @param rc[out] An lvalue of type int that holds the status result of parsing the
+ *                firmware device ID record
+ *
+ * @p rc is set to 0 on successful decode. Otherwise, on error, @p rc is set to:
+ * - -EINVAL if parameters values are invalid
+ * - -EOVERFLOW if the package layout exceeds the bounds of the package buffer
+ * - -EPROTO if package metadata doesn't conform to specification constraints
+ *
+ * Example use of the macro is as follows:
+ *
+ * @code
+ * DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+ *
+ * struct pldm_package_downstream_device_id_record ddrec;
+ * struct pldm_package_firmware_device_id_record fdrec;
+ * pldm_package_header_information_pad hdr;
+ * struct pldm_package_iter iter;
+ * int rc;
+ *
+ * rc = decode_pldm_firmware_update_package(package, in, &pin, &hdr,
+ * 					 &iter);
+ * if (rc < 0) { ... }
+ *
+ * foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc) {
+ *     struct pldm_descriptor desc;
+ * 	   ...
+ *     foreach_pldm_package_firmware_device_id_record_descriptor(
+ *             iter, fdrec, desc, rc) {
+ *         ...
+ *     }
+ *     if (rc) { ... }
+ * }
+ * if (rc) { ... }
+ *
+ * foreach_pldm_package_downstream_device_id_record(iter, ddrec, rc) {
+ * 	   // Do something with ddrec
+ * }
+ * if (rc) {
+ * 	   // Handle parsing failure for ddrec
+ * }
+ * @endcode
+ */
+#define foreach_pldm_package_downstream_device_id_record(iter, rec, rc)          \
+	for ((rc) = pldm_package_downstream_device_id_record_iter_init(          \
+		     (iter).hdr, &(iter).fds, &(iter).dds);                      \
+	     !(rc) &&                                                            \
+	     !pldm_package_downstream_device_id_record_iter_end(                 \
+		     &(iter).dds) &&                                             \
+	     !((rc) = decode_pldm_package_downstream_device_id_record_from_iter( \
+		       (iter).hdr, &(iter).dds, &(rec)));                        \
+	     pldm_package_downstream_device_id_record_iter_next(&(iter).dds))
+
+LIBPLDM_ITERATOR
+struct pldm_descriptor_iter
+pldm_package_downstream_device_id_record_descriptor_iter_init(
+	struct pldm_package_downstream_device_id_record_iter *iter,
+	struct pldm_package_downstream_device_id_record *rec)
+{
+	(void)iter;
+	return (struct pldm_descriptor_iter){ &rec->record_descriptors,
+					      rec->descriptor_count };
+}
+
+/**
+ * @brief Iterate over the descriptors in a package's downstream device ID record
+ *
+ * @param iter[in,out] The lvalue for the instance of @ref "struct pldm_package_iter"
+ *             initialised by @ref decode_pldm_firmware_update_package
+ * @param rec[in] An lvalue of type @ref "struct pldm_package_downstream_device_id_record"
+ * @param desc[out] An lvalue of type @ref "struct pldm_descriptor" that holds
+ *                  the parsed descriptor
+ * @param rc[out] An lvalue of type int that holds the status result of parsing the
+ *                downstream device ID record
+ *
+ * @p rc is set to 0 on successful decode. Otherwise, on error, @p rc is set to:
+ * - -EINVAL if parameters values are invalid
+ * - -EOVERFLOW if the package layout exceeds the bounds of the package buffer
+ * - -EPROTO if package metadata doesn't conform to specification constraints
+ *
+ * Example use of the macro is as follows:
+ *
+ * @code
+ * DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+ *
+ * struct pldm_package_downstream_device_id_record ddrec;
+ * struct pldm_package_firmware_device_id_record fdrec;
+ * pldm_package_header_information_pad hdr;
+ * struct pldm_package_iter iter;
+ * int rc;
+ *
+ * rc = decode_pldm_firmware_update_package(package, in, &pin, &hdr,
+ * 					 &iter);
+ * if (rc < 0) { ... }
+ *
+ * foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc) {
+ *     struct pldm_descriptor desc;
+ * 	   ...
+ *     foreach_pldm_package_firmware_device_id_record_descriptor(
+ *             iter, fdrec, desc, rc) {
+ *         ...
+ *     }
+ *     if (rc) { ... }
+ * }
+ * if (rc) { ... }
+ *
+ * foreach_pldm_package_downstream_device_id_record(iter, ddrec, rc) {
+ *     struct pldm_descriptor desc;
+ * 	   ...
+ *     foreach_pldm_package_downstream_device_id_record_descriptor(
+ *             iter, ddrec, desc, rc)
+ *     {
+ *         // Do something with desc
+ *     }
+ *     if (rc) {
+ *         // Handle parsing failure for desc
+ *     }
+ * }
+ * if (rc) { ... }
+ * @endcode
+ */
+#define foreach_pldm_package_downstream_device_id_record_descriptor(iter, rec,      \
+								    desc, rc)       \
+	for (struct pldm_descriptor_iter desc##_iter =                              \
+		     ((rc) = 0,                                                     \
+		     pldm_package_downstream_device_id_record_descriptor_iter_init( \
+			      &(iter).dds, &(rec)));                                \
+	     (!pldm_descriptor_iter_end(&(desc##_iter))) &&                         \
+	     !((rc) = decode_pldm_descriptor_from_iter(&(desc##_iter),              \
+						       &(desc)));                   \
+	     pldm_descriptor_iter_next(&(desc##_iter)))
+
+LIBPLDM_ITERATOR
+bool pldm_package_component_image_information_iter_end(
+	const struct pldm_package_component_image_information_iter *iter)
+{
+	return (iter->entries == 0);
+}
+
+LIBPLDM_ITERATOR
+bool pldm_package_component_image_information_iter_next(
+	struct pldm_package_component_image_information_iter *iter)
+{
+	if (!iter->entries) {
+		return false;
+	}
+	iter->entries--;
+	return true;
+}
+
+int pldm_package_component_image_information_iter_init(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_downstream_device_id_record_iter *dds,
+	struct pldm_package_component_image_information_iter *infos);
+
+int decode_pldm_package_component_image_information_from_iter(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_component_image_information_iter *iter,
+	struct pldm_package_component_image_information *info);
+
+/**
+ * @brief Iterate over the component image information contained in the package
+ *
+ * @param iter[in,out] The lvalue for the instance of @ref "struct pldm_package_iter"
+ *             initialised by @ref decode_pldm_firmware_update_package
+ * @param rec[in] An lvalue of type @ref "struct pldm_package_downstream_device_id_record"
+ * @param desc[out] An lvalue of type @ref "struct pldm_descriptor" that holds
+ *                  the parsed descriptor
+ * @param rc[out] An lvalue of type int that holds the status result of parsing the
+ *                downstream device ID record
+ *
+ * @p rc is set to 0 on successful decode. Otherwise, on error, @p rc is set to:
+ * - -EINVAL if parameters values are invalid
+ * - -EOVERFLOW if the package layout exceeds the bounds of the package buffer
+ * - -EPROTO if package metadata doesn't conform to specification constraints
+ *
+ * Example use of the macro is as follows:
+ *
+ * @code
+ * DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+ *
+ * struct pldm_package_downstream_device_id_record ddrec;
+ * struct pldm_package_component_image_information info;
+ * struct pldm_package_firmware_device_id_record fdrec;
+ * pldm_package_header_information_pad hdr;
+ * struct pldm_package_iter iter;
+ * int rc;
+ *
+ * rc = decode_pldm_firmware_update_package(package, in, &pin, &hdr,
+ * 					 &iter);
+ * if (rc < 0) { ... }
+ *
+ * foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc) {
+ *     struct pldm_descriptor desc;
+ * 	   ...
+ *     foreach_pldm_package_firmware_device_id_record_descriptor(
+ *             iter, fdrec, desc, rc) {
+ *         ...
+ *     }
+ *     if (rc) { ... }
+ * }
+ * if (rc) { ... }
+ *
+ * foreach_pldm_package_downstream_device_id_record(iter, ddrec, rc) {
+ *     struct pldm_descriptor desc;
+ * 	   ...
+ *     foreach_pldm_package_downstream_device_id_record_descriptor(
+ *             iter, ddrec, desc, rc) {
+ *         ...
+ *     }
+ *     if (rc) { ... }
+ * }
+ * if (rc) { ... }
+ *
+ * foreach_pldm_package_component_image_information(iter, info, rc) {
+ *     // Do something with info
+ * }
+ * if (rc) {
+ * 	   // Handle parsing failure for info
+ * }
+ * @endcode
+ */
+#define foreach_pldm_package_component_image_information(iter, info, rc)         \
+	for ((rc) = pldm_package_component_image_information_iter_init(          \
+		     (iter).hdr, &(iter).dds, &(iter).infos);                    \
+	     !(rc) &&                                                            \
+	     !pldm_package_component_image_information_iter_end(                 \
+		     &(iter).infos) &&                                           \
+	     !((rc) = decode_pldm_package_component_image_information_from_iter( \
+		       (iter).hdr, &(iter).infos, &(info)));                     \
+	     pldm_package_component_image_information_iter_next(                 \
+		     &(iter).infos))
+
+/**
+ * Declare consumer support for at most revision 1 of the firmware update
+ * package header
+ *
+ * @param name The name for the pin object
+ *
+ * The pin object must be provided to @ref decode_pldm_firmware_update_package
+ */
+#define DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR01H(name)                             \
+	struct pldm_package_format_pin name = { \
+		.meta = { \
+			.magic = ( \
+				LIBPLDM_SIZEAT(struct pldm__package_header_information, package) + \
+				LIBPLDM_SIZEAT(struct pldm_package_firmware_device_id_record, firmware_device_package_data) + \
+				LIBPLDM_SIZEAT(struct pldm_descriptor, descriptor_data) + \
+				LIBPLDM_SIZEAT(struct pldm_package_component_image_information, component_version_string) + \
+				LIBPLDM_SIZEAT(struct pldm_package_iter, infos) \
+			), \
+			.version = 0u, \
+		}, \
+		.format = { \
+			.identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_0, \
+			.revision = PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR01H, \
+		} \
+	}
+
+/**
+ * Declare consumer support for at most revision 2 of the firmware update
+ * package header
+ *
+ * @param name The name for the pin object
+ *
+ * The pin object must be provided to @ref decode_pldm_firmware_update_package
+ */
+#define DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(name)                             \
+	struct pldm_package_format_pin name = { \
+		.meta = { \
+			.magic = ( \
+				LIBPLDM_SIZEAT(struct pldm__package_header_information, package) + \
+				LIBPLDM_SIZEAT(struct pldm_package_firmware_device_id_record, firmware_device_package_data) + \
+				LIBPLDM_SIZEAT(struct pldm_descriptor, descriptor_data) + \
+				LIBPLDM_SIZEAT(struct pldm_package_downstream_device_id_record, package_data) + \
+				LIBPLDM_SIZEAT(struct pldm_package_component_image_information, component_version_string) + \
+				LIBPLDM_SIZEAT(struct pldm_package_iter, infos) \
+			), \
+			.version = 0u, \
+		}, \
+		.format = { \
+			.identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1, \
+			.revision = PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H, \
+		} \
+	}
+
 #ifdef __cplusplus
 }
 #endif
diff --git a/include/libpldm/pldm_types.h b/include/libpldm/pldm_types.h
index e6c8985..a32e0ba 100644
--- a/include/libpldm/pldm_types.h
+++ b/include/libpldm/pldm_types.h
@@ -163,4 +163,6 @@
 
 typedef float real32_t;
 
+typedef uint8_t pldm_uuid[16];
+
 #endif /* PLDM_TYPES_H */
diff --git a/src/dsp/firmware_update.c b/src/dsp/firmware_update.c
index ac7937e..a074472 100644
--- a/src/dsp/firmware_update.c
+++ b/src/dsp/firmware_update.c
@@ -1,8 +1,11 @@
 /* SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */
 #include "api.h"
+#include "array.h"
+#include "compiler.h"
 #include "dsp/base.h"
 #include "msgbuf.h"
 #include <libpldm/base.h>
+#include <libpldm/compiler.h>
 #include <libpldm/firmware_update.h>
 #include <libpldm/utils.h>
 
@@ -327,32 +330,43 @@
 	}
 }
 
-// Identifier for a valid PLDM Firmware Update Package for Version 1.0.x as per
-// spec
-static const uint8_t PLDM_FWUP_HDR_IDENTIFIER_V1[PLDM_FWUP_UUID_LENGTH] = {
-	0xF0, 0x18, 0x87, 0x8C, 0xCB, 0x7D, 0x49, 0x43,
-	0x98, 0x00, 0xA0, 0x2F, 0x05, 0x9A, 0xCA, 0x02
-};
-
-/* TODO: Remove struct pldm_package_header_information from public header, rename padded struct, drop typedef */
-struct pldm__package_header_information {
-	uint8_t package_header_identifier[PLDM_FWUP_UUID_LENGTH];
-	uint8_t package_header_format_revision;
-	uint8_t package_release_date_time[PLDM_TIMESTAMP104_SIZE];
-	uint16_t component_bitmap_bit_length;
-	uint8_t package_version_string_type;
-	struct variable_field package_version_string;
-	struct variable_field areas;
-	struct variable_field images;
-};
-typedef struct pldm__package_header_information
-	pldm_package_header_information_pad;
-
 #define PLDM_FWUP_PACKAGE_HEADER_FIXED_SIZE 36
-static int decode_pldm_package_header_info_errno(
-	const void *data, size_t length,
-	pldm_package_header_information_pad *header)
+LIBPLDM_CC_NONNULL
+static int
+decode_pldm_package_header_info_errno(const void *data, size_t length,
+				      const struct pldm_package_format_pin *pin,
+				      pldm_package_header_information_pad *hdr)
 {
+	static const struct pldm_package_header_format_revision_info {
+		pldm_uuid identifier;
+		size_t magic;
+	} revision_info[1 + PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H] = {
+		[0] = {
+			.identifier = {0},
+			.magic = 0,
+		},
+		[PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR01H] = { /* PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR01H */
+			.identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_0,
+			.magic =
+				LIBPLDM_SIZEAT(struct pldm__package_header_information, package) +
+				LIBPLDM_SIZEAT(struct pldm_package_firmware_device_id_record, firmware_device_package_data) +
+				LIBPLDM_SIZEAT(struct pldm_descriptor, descriptor_data) +
+				LIBPLDM_SIZEAT(struct pldm_package_component_image_information, component_version_string) +
+				LIBPLDM_SIZEAT(struct pldm_package_iter, infos)
+		},
+		[PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H] = { /* PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H */
+			.identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1,
+			.magic =
+				LIBPLDM_SIZEAT(struct pldm__package_header_information, package) +
+				LIBPLDM_SIZEAT(struct pldm_package_firmware_device_id_record, firmware_device_package_data) +
+				LIBPLDM_SIZEAT(struct pldm_descriptor, descriptor_data) +
+				LIBPLDM_SIZEAT(struct pldm_package_downstream_device_id_record, package_data) +
+				LIBPLDM_SIZEAT(struct pldm_package_component_image_information, component_version_string) +
+				LIBPLDM_SIZEAT(struct pldm_package_iter, infos),
+		},
+	};
+
+	const struct pldm_package_header_format_revision_info *info;
 	uint32_t package_header_checksum = 0;
 	size_t package_header_variable_size;
 	size_t package_header_payload_size;
@@ -362,7 +376,28 @@
 	int checksums = 1;
 	int rc;
 
-	if (!data || !header) {
+	if (pin->meta.version > 0) {
+		return -ENOTSUP;
+	}
+
+	if (pin->format.revision == 0) {
+		return -EINVAL;
+	}
+
+	if (pin->format.revision > PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H) {
+		return -ENOTSUP;
+	}
+	static_assert(ARRAY_SIZE(revision_info) ==
+			      1 + PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H,
+		      "Mismatched array bounds test");
+
+	info = &revision_info[pin->format.revision];
+	if (memcmp(&pin->format.identifier, info->identifier,
+		   sizeof(info->identifier)) != 0) {
+		return -ENOTSUP;
+	}
+
+	if (pin->meta.magic != info->magic) {
 		return -EINVAL;
 	}
 
@@ -372,29 +407,31 @@
 		return rc;
 	}
 
-	rc = pldm_msgbuf_extract_array(
-		buf, sizeof(header->package_header_identifier),
-		header->package_header_identifier,
-		sizeof(header->package_header_identifier));
+	rc = pldm_msgbuf_extract_array(buf,
+				       sizeof(hdr->package_header_identifier),
+				       hdr->package_header_identifier,
+				       sizeof(hdr->package_header_identifier));
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
 
-	static_assert(sizeof(PLDM_FWUP_HDR_IDENTIFIER_V1) ==
-			      sizeof(header->package_header_identifier),
-		      "UUID field size");
-	if (memcmp(PLDM_FWUP_HDR_IDENTIFIER_V1,
-		   header->package_header_identifier,
-		   sizeof(PLDM_FWUP_HDR_IDENTIFIER_V1)) != 0) {
+	if (memcmp(revision_info[PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR01H]
+			   .identifier,
+		   hdr->package_header_identifier,
+		   sizeof(hdr->package_header_identifier)) != 0 &&
+	    memcmp(revision_info[PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H]
+			   .identifier,
+		   hdr->package_header_identifier,
+		   sizeof(hdr->package_header_identifier)) != 0) {
 		return pldm_msgbuf_discard(buf, -ENOTSUP);
 	}
 
-	rc = pldm_msgbuf_extract(buf, header->package_header_format_revision);
+	rc = pldm_msgbuf_extract(buf, hdr->package_header_format_revision);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
-	if (header->package_header_format_revision != 0x01) {
-		return pldm_msgbuf_discard(buf, -EBADMSG);
+	if (hdr->package_header_format_revision > pin->format.revision) {
+		return pldm_msgbuf_discard(buf, -ENOTSUP);
 	}
 
 	rc = pldm_msgbuf_extract(buf, package_header_size);
@@ -402,64 +439,60 @@
 		return pldm_msgbuf_discard(buf, rc);
 	}
 
-	rc = pldm_msgbuf_extract_array(
-		buf, sizeof(header->package_release_date_time),
-		header->package_release_date_time,
-		sizeof(header->package_release_date_time));
+	rc = pldm_msgbuf_extract_array(buf,
+				       sizeof(hdr->package_release_date_time),
+				       hdr->package_release_date_time,
+				       sizeof(hdr->package_release_date_time));
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
 
-	rc = pldm_msgbuf_extract(buf, header->component_bitmap_bit_length);
+	rc = pldm_msgbuf_extract(buf, hdr->component_bitmap_bit_length);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
-	if (header->component_bitmap_bit_length & 7) {
+	if (hdr->component_bitmap_bit_length & 7) {
 		return pldm_msgbuf_discard(buf, -EPROTO);
 	}
 
-	rc = pldm_msgbuf_extract(buf, header->package_version_string_type);
+	rc = pldm_msgbuf_extract(buf, hdr->package_version_string_type);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
-	if (!is_string_type_valid(header->package_version_string_type)) {
+	if (!is_string_type_valid(hdr->package_version_string_type)) {
 		return pldm_msgbuf_discard(buf, -EPROTO);
 	}
 
 	rc = pldm_msgbuf_extract_uint8_to_size(
-		buf, header->package_version_string.length);
+		buf, hdr->package_version_string.length);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
-	if (header->package_version_string.length == 0) {
-		return pldm_msgbuf_discard(buf, -EBADMSG);
-	}
 
-	pldm_msgbuf_span_required(buf, header->package_version_string.length,
-				  (void **)&header->package_version_string.ptr);
+	pldm_msgbuf_span_required(buf, hdr->package_version_string.length,
+				  (void **)&hdr->package_version_string.ptr);
 
 	if (package_header_size < (PLDM_FWUP_PACKAGE_HEADER_FIXED_SIZE + 3 +
 				   checksums * sizeof(uint32_t))) {
-		return pldm_msgbuf_discard(buf, -EBADMSG);
+		return pldm_msgbuf_discard(buf, -EOVERFLOW);
 	}
 	package_header_payload_size =
 		package_header_size - (checksums * sizeof(uint32_t));
 	package_header_variable_size = package_header_payload_size -
 				       PLDM_FWUP_PACKAGE_HEADER_FIXED_SIZE;
 
-	if (package_header_variable_size <
-	    header->package_version_string.length) {
+	if (package_header_variable_size < hdr->package_version_string.length) {
 		return pldm_msgbuf_discard(buf, -EOVERFLOW);
 	}
 
 	package_header_areas_size = package_header_variable_size -
-				    header->package_version_string.length;
+				    hdr->package_version_string.length;
 	rc = pldm_msgbuf_span_required(buf, package_header_areas_size,
-				       (void **)&header->areas.ptr);
+				       (void **)&hdr->areas.ptr);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
-	header->areas.length = package_header_areas_size;
+	hdr->areas.length = package_header_areas_size;
 
 	pldm_msgbuf_extract(buf, package_header_checksum);
 
@@ -468,11 +501,19 @@
 		return rc;
 	}
 
+	// TODO: pldm_edac_crc32_test(uint32_t expected, const void *data, size_t len)
 	if (package_header_checksum !=
-	    crc32(data, package_header_payload_size)) {
+	    pldm_edac_crc32(data, package_header_payload_size)) {
+#if 0
+		printf("checksum failure, expected: %#08" PRIx32 ", found: %#08" PRIx32 "\n", package_header_checksum, pldm_edac_crc32(data, package_header_payload_size));
+#endif
 		return -EUCLEAN;
 	}
 
+	/* We stash these to resolve component images later */
+	hdr->package.ptr = data;
+	hdr->package.length = length;
+
 	return 0;
 }
 
@@ -482,81 +523,65 @@
 	struct pldm_package_header_information *package_header_info,
 	struct variable_field *package_version_str)
 {
-	pldm_package_header_information_pad header;
+	DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR01H(pin);
+	pldm_package_header_information_pad hdr;
 	int rc;
 
 	if (!data || !package_header_info || !package_version_str) {
 		return PLDM_ERROR_INVALID_DATA;
 	}
 
-	rc = decode_pldm_package_header_info_errno(data, length, &header);
+	rc = decode_pldm_package_header_info_errno(data, length, &pin, &hdr);
 	if (rc < 0) {
 		return pldm_xlate_errno(rc);
 	}
 
 	static_assert(sizeof(package_header_info->uuid) ==
-			      sizeof(header.package_header_identifier),
+			      sizeof(hdr.package_header_identifier),
 		      "UUID field size");
-	memcpy(package_header_info->uuid, header.package_header_identifier,
-	       sizeof(header.package_header_identifier));
+	memcpy(package_header_info->uuid, hdr.package_header_identifier,
+	       sizeof(hdr.package_header_identifier));
 	package_header_info->package_header_format_version =
-		header.package_header_format_revision;
+		hdr.package_header_format_revision;
 	memcpy(&package_header_info->package_header_size, data + 17,
 	       sizeof(package_header_info->package_header_size));
 	LE16TOH(package_header_info->package_header_size);
 	static_assert(sizeof(package_header_info->package_release_date_time) ==
-			      sizeof(header.package_release_date_time),
+			      sizeof(hdr.package_release_date_time),
 		      "TIMESTAMP104 field size");
 	memcpy(package_header_info->package_release_date_time,
-	       header.package_release_date_time,
-	       sizeof(header.package_release_date_time));
+	       hdr.package_release_date_time,
+	       sizeof(hdr.package_release_date_time));
 	package_header_info->component_bitmap_bit_length =
-		header.component_bitmap_bit_length;
+		hdr.component_bitmap_bit_length;
 	package_header_info->package_version_string_type =
-		header.package_version_string_type;
+		hdr.package_version_string_type;
 	package_header_info->package_version_string_length =
-		header.package_version_string.length;
-	*package_version_str = header.package_version_string;
+		hdr.package_version_string.length;
+	*package_version_str = hdr.package_version_string;
 
 	return PLDM_SUCCESS;
 }
 
-struct pldm_component_bitmap {
-	struct variable_field bitmap;
-};
-
-/* TODO: Remove struct pldm_firmware_device_id_record from public header, rename padded struct, drop typedef */
-struct pldm__firmware_device_id_record {
-	uint8_t descriptor_count;
-	bitfield32_t device_update_option_flags;
-	uint8_t component_image_set_version_string_type;
-	struct variable_field component_image_set_version_string;
-	struct pldm_component_bitmap applicable_components;
-	struct variable_field record_descriptors;
-	struct variable_field firmware_device_package_data;
-};
-typedef struct pldm__firmware_device_id_record
-	pldm_firmware_device_id_record_pad;
-
 /* Currently only used for decode_firmware_device_id_record_errno() */
 static int pldm_msgbuf_init_dynamic_uint16(struct pldm_msgbuf *buf, size_t req,
-					   const void *data, size_t len)
+					   void *data, size_t len,
+					   void **tail_data, size_t *tail_len)
 {
-	uint16_t dyn;
-	void *start;
+	size_t dyn_length;
+	void *dyn_start;
 	int rc;
 
-	/*
-	 * Extract the record length from the first field, then reinitialise the msgbuf
-	 * after determining that it's safe to do so
-	 */
-
 	rc = pldm_msgbuf_init_errno(buf, req, data, len);
 	if (rc) {
 		return rc;
 	}
+	/*
+	 * Extract the record length from the first field, then reinitialise the msgbuf
+	 * after determining that it's safe to do so
+	 */
 
-	rc = pldm_msgbuf_extract(buf, dyn);
+	rc = pldm_msgbuf_extract_uint16_to_size(buf, dyn_length);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
@@ -572,7 +597,12 @@
 	}
 
 	/* Ensure there's no arithmetic funkiness and the span is within buffer bounds */
-	rc = pldm_msgbuf_span_required(buf, dyn, &start);
+	rc = pldm_msgbuf_span_required(buf, dyn_length, &dyn_start);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	rc = pldm_msgbuf_span_remaining(buf, tail_data, tail_len);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
@@ -582,34 +612,31 @@
 		return rc;
 	}
 
-	return pldm_msgbuf_init_errno(buf, req, start, dyn);
+	return pldm_msgbuf_init_errno(buf, req, dyn_start, dyn_length);
 }
 
 #define PLDM_FWUP_FIRMWARE_DEVICE_ID_RECORD_MIN_SIZE 11
-static int decode_firmware_device_id_record_errno(
-	const void *data, size_t length,
-	pldm_package_header_information_pad *header,
-	pldm_firmware_device_id_record_pad *rec)
+static int decode_pldm_package_firmware_device_id_record_errno(
+	const pldm_package_header_information_pad *hdr,
+	struct variable_field *field,
+	struct pldm_package_firmware_device_id_record *rec)
 {
 	PLDM_MSGBUF_DEFINE_P(buf);
 	uint16_t record_len = 0;
 	int rc;
 
-	if (!data || !header || !rec) {
+	if (!hdr || !field || !rec || !field->ptr) {
 		return -EINVAL;
 	}
 
-	if (header->package_header_format_revision != 1) {
-		return -ENOTSUP;
-	}
-
-	if (header->component_bitmap_bit_length & 7) {
+	if (hdr->component_bitmap_bit_length & 7) {
 		return -EPROTO;
 	}
 
 	rc = pldm_msgbuf_init_dynamic_uint16(
-		buf, PLDM_FWUP_FIRMWARE_DEVICE_ID_RECORD_MIN_SIZE, data,
-		length);
+		buf, PLDM_FWUP_FIRMWARE_DEVICE_ID_RECORD_MIN_SIZE,
+		(void *)field->ptr, field->length, (void **)&field->ptr,
+		&field->length);
 	if (rc) {
 		return rc;
 	}
@@ -645,13 +672,13 @@
 	}
 
 	rc = pldm_msgbuf_span_required(
-		buf, header->component_bitmap_bit_length / 8,
+		buf, hdr->component_bitmap_bit_length / 8,
 		(void **)&rec->applicable_components.bitmap.ptr);
 	if (rc) {
 		return pldm_msgbuf_discard(buf, rc);
 	}
 	rec->applicable_components.bitmap.length =
-		header->component_bitmap_bit_length / 8;
+		hdr->component_bitmap_bit_length / 8;
 
 	pldm_msgbuf_span_required(
 		buf, rec->component_image_set_version_string.length,
@@ -681,8 +708,8 @@
 	struct variable_field *record_descriptors,
 	struct variable_field *fw_device_pkg_data)
 {
-	pldm_package_header_information_pad header = { 0 };
-	pldm_firmware_device_id_record_pad rec = { 0 };
+	struct pldm_package_firmware_device_id_record rec;
+	pldm_package_header_information_pad hdr;
 	int rc;
 
 	if (!data || !fw_device_id_record || !applicable_components ||
@@ -691,12 +718,12 @@
 		return PLDM_ERROR_INVALID_DATA;
 	}
 
-	header.component_bitmap_bit_length = component_bitmap_bit_length;
-	memcpy(header.package_header_identifier, PLDM_FWUP_HDR_IDENTIFIER_V1,
-	       sizeof(PLDM_FWUP_HDR_IDENTIFIER_V1));
-	header.package_header_format_revision = 1;
-	rc = decode_firmware_device_id_record_errno(data, length, &header,
-						    &rec);
+	hdr.package_header_format_revision =
+		PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR01H;
+	hdr.component_bitmap_bit_length = component_bitmap_bit_length;
+
+	rc = decode_pldm_package_firmware_device_id_record_errno(
+		&hdr, &(struct variable_field){ data, length }, &rec);
 	if (rc < 0) {
 		return pldm_xlate_errno(rc);
 	}
@@ -3128,3 +3155,310 @@
 
 	return pldm_msgbuf_complete_used(buf, *payload_length, payload_length);
 }
+
+LIBPLDM_ABI_TESTING
+int decode_pldm_firmware_update_package(
+	const void *data, size_t length,
+	const struct pldm_package_format_pin *pin,
+	pldm_package_header_information_pad *hdr,
+	struct pldm_package_iter *iter)
+{
+	if (!data || !pin || !hdr || !iter) {
+		return -EINVAL;
+	}
+
+	iter->hdr = hdr;
+
+	return decode_pldm_package_header_info_errno(data, length, pin, hdr);
+}
+
+LIBPLDM_ABI_TESTING
+int pldm_package_firmware_device_id_record_iter_init(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_firmware_device_id_record_iter *iter)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!hdr || !iter || !hdr->areas.ptr) {
+		return -EINVAL;
+	}
+
+	iter->field = hdr->areas;
+
+	/* Extract the fd record id count */
+	rc = pldm_msgbuf_init_errno(buf, 1, iter->field.ptr,
+				    iter->field.length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract_uint8_to_size(buf, iter->entries);
+	pldm_msgbuf_span_remaining(buf, (void **)&iter->field.ptr,
+				   &iter->field.length);
+
+	return pldm_msgbuf_complete(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int decode_pldm_package_firmware_device_id_record_from_iter(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_firmware_device_id_record_iter *iter,
+	struct pldm_package_firmware_device_id_record *rec)
+{
+	return decode_pldm_package_firmware_device_id_record_errno(
+		hdr, &iter->field, rec);
+}
+
+LIBPLDM_ABI_TESTING
+int pldm_package_downstream_device_id_record_iter_init(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_firmware_device_id_record_iter *fds,
+	struct pldm_package_downstream_device_id_record_iter *dds)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!hdr || !fds || !dds || !fds->field.ptr) {
+		return -EINVAL;
+	}
+
+	dds->field = fds->field;
+	fds->field.ptr = NULL;
+	fds->field.length = 0;
+
+	/* Downstream device ID records aren't specified in revision 1 */
+	if (hdr->package_header_format_revision <
+	    PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H) {
+		dds->entries = 0;
+		return 0;
+	}
+
+	/* Extract the dd record id count */
+	rc = pldm_msgbuf_init_errno(buf, 1, dds->field.ptr, dds->field.length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract_uint8_to_size(buf, dds->entries);
+	pldm_msgbuf_span_remaining(buf, (void **)&dds->field.ptr,
+				   &dds->field.length);
+
+	return pldm_msgbuf_complete(buf);
+}
+
+#define PLDM_FWUP_DOWNSTREAM_DEVICE_ID_RECORD_MIN_SIZE 11
+LIBPLDM_ABI_TESTING
+int decode_pldm_package_downstream_device_id_record_from_iter(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_downstream_device_id_record_iter *iter,
+	struct pldm_package_downstream_device_id_record *rec)
+{
+	PLDM_MSGBUF_DEFINE_P(buf);
+	uint16_t record_len = 0;
+	int rc;
+
+	if (!hdr || !iter || !rec || !iter->field.ptr) {
+		return -EINVAL;
+	}
+
+	if (hdr->package_header_format_revision <
+	    PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H) {
+		/* Should not be reached due to corresponding test in iter initialisation */
+		return -ENOTSUP;
+	}
+
+	if (hdr->component_bitmap_bit_length & 7) {
+		return -EPROTO;
+	}
+
+	rc = pldm_msgbuf_init_dynamic_uint16(
+		buf, PLDM_FWUP_DOWNSTREAM_DEVICE_ID_RECORD_MIN_SIZE,
+		(void *)iter->field.ptr, iter->field.length,
+		(void **)&iter->field.ptr, &iter->field.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	pldm_msgbuf_extract(buf, record_len);
+	pldm_msgbuf_extract(buf, rec->descriptor_count);
+
+	rc = pldm_msgbuf_extract(buf, rec->update_option_flags.value);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	rc = pldm_msgbuf_extract(
+		buf, rec->self_contained_activation_min_version_string_type);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+	if (!is_string_type_valid(
+		    rec->self_contained_activation_min_version_string_type)) {
+		return pldm_msgbuf_discard(buf, -EPROTO);
+	}
+
+	rc = pldm_msgbuf_extract_uint8_to_size(
+		buf, rec->self_contained_activation_min_version_string.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	rc = pldm_msgbuf_extract_uint16_to_size(buf, rec->package_data.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	rc = pldm_msgbuf_span_required(
+		buf, hdr->component_bitmap_bit_length / 8,
+		(void **)&rec->applicable_components.bitmap.ptr);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+	rec->applicable_components.bitmap.length =
+		hdr->component_bitmap_bit_length / 8;
+
+	pldm_msgbuf_span_required(
+		buf, rec->self_contained_activation_min_version_string.length,
+		(void **)&rec->self_contained_activation_min_version_string.ptr);
+	if (rec->update_option_flags.bits.bit0) {
+		pldm_msgbuf_extract(
+			buf,
+			rec->self_contained_activation_min_version_comparison_stamp);
+	} else {
+		rec->self_contained_activation_min_version_comparison_stamp = 0;
+	}
+
+	pldm_msgbuf_span_until(buf, rec->package_data.length,
+			       (void **)&rec->record_descriptors.ptr,
+			       &rec->record_descriptors.length);
+
+	pldm_msgbuf_span_required(buf, rec->package_data.length,
+				  (void **)&rec->package_data.ptr);
+
+	return pldm_msgbuf_complete_consumed(buf);
+}
+
+LIBPLDM_ABI_TESTING
+int pldm_package_component_image_information_iter_init(
+	const pldm_package_header_information_pad *hdr LIBPLDM_CC_UNUSED,
+	struct pldm_package_downstream_device_id_record_iter *dds,
+	struct pldm_package_component_image_information_iter *infos)
+{
+	uint16_t component_image_count;
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!dds || !infos) {
+		return -EINVAL;
+	}
+
+	infos->field = dds->field;
+	dds->field.ptr = NULL;
+	dds->field.length = 0;
+
+	/* Extract the component image count */
+	rc = pldm_msgbuf_init_errno(buf, 1, infos->field.ptr,
+				    infos->field.length);
+	if (rc) {
+		return rc;
+	}
+
+	rc = pldm_msgbuf_extract(buf, component_image_count);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+	infos->entries = component_image_count;
+
+	pldm_msgbuf_span_remaining(buf, (void **)&infos->field.ptr,
+				   &infos->field.length);
+
+	return pldm_msgbuf_complete(buf);
+}
+
+#define PLDM_FWUP_COMPONENT_IMAGE_INFORMATION_MIN_SIZE 22
+LIBPLDM_ABI_TESTING
+int decode_pldm_package_component_image_information_from_iter(
+	const pldm_package_header_information_pad *hdr,
+	struct pldm_package_component_image_information_iter *iter,
+	struct pldm_package_component_image_information *info)
+{
+	uint32_t component_location_offset = 0;
+	uint32_t component_size = 0;
+	PLDM_MSGBUF_DEFINE_P(buf);
+	int rc;
+
+	if (!hdr || !iter || !info || !iter->field.ptr) {
+		return -EINVAL;
+	}
+
+	if (hdr->component_bitmap_bit_length & 7) {
+		return -EPROTO;
+	}
+
+	rc = pldm_msgbuf_init_errno(
+		buf, PLDM_FWUP_COMPONENT_IMAGE_INFORMATION_MIN_SIZE,
+		iter->field.ptr, iter->field.length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_extract(buf, info->component_classification);
+	pldm_msgbuf_extract(buf, info->component_identifier);
+	pldm_msgbuf_extract(buf, info->component_comparison_stamp);
+	pldm_msgbuf_extract(buf, info->component_options.value);
+	pldm_msgbuf_extract(buf,
+			    info->requested_component_activation_method.value);
+	pldm_msgbuf_extract(buf, component_location_offset);
+	pldm_msgbuf_extract(buf, component_size);
+
+	rc = pldm_msgbuf_extract(buf, info->component_version_string_type);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+	if (!is_string_type_valid(info->component_version_string_type)) {
+		return pldm_msgbuf_discard(buf, -EPROTO);
+	}
+
+	rc = pldm_msgbuf_extract_uint8_to_size(
+		buf, info->component_version_string.length);
+	if (rc) {
+		return pldm_msgbuf_discard(buf, rc);
+	}
+
+	pldm_msgbuf_span_required(buf, info->component_version_string.length,
+				  (void **)&info->component_version_string.ptr);
+
+	pldm_msgbuf_span_remaining(buf, (void **)&iter->field.ptr,
+				   &iter->field.length);
+
+	rc = pldm_msgbuf_complete_consumed(buf);
+	if (rc) {
+		return rc;
+	}
+
+	if (info->component_classification > 0x000d &&
+	    info->component_classification < 0x8000) {
+		return -EPROTO;
+	}
+
+	/* Resolve the component image in memory */
+	rc = pldm_msgbuf_init_errno(buf, 0, hdr->package.ptr,
+				    hdr->package.length);
+	if (rc) {
+		return rc;
+	}
+
+	pldm_msgbuf_span_required(buf, component_location_offset, NULL);
+	pldm_msgbuf_span_required(buf, component_size,
+				  (void **)&info->component_image.ptr);
+
+	rc = pldm_msgbuf_complete(buf);
+	if (rc) {
+		return rc;
+	}
+
+	info->component_image.length = component_size;
+
+	return 0;
+}
diff --git a/tests/dsp/firmware_update.cpp b/tests/dsp/firmware_update.cpp
index 05b7546..9566af6 100644
--- a/tests/dsp/firmware_update.cpp
+++ b/tests/dsp/firmware_update.cpp
@@ -48,6 +48,13 @@
                                              0x05, 0x9a, 0xca, 0x02};
 
 static constexpr uint8_t PLDM_FWUP_PACKAGE_HEADER_FORMAT_REVISION_V1_0 = 0x01;
+
+static constexpr std::array<uint8_t, PLDM_FWUP_UUID_LENGTH>
+    PLDM_FWUP_PACKAGE_HEADER_IDENTIFIER_V1_1{
+        0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18,
+        0xa0, 0x30, 0xfc, 0x8a, 0x56, 0x58, 0x7d, 0x5a,
+    };
+
 static constexpr size_t PLDM_FWUP_PACKAGE_HEADER_EMPTY_SIZE_V1_0 = 43;
 
 static constexpr std::array<uint8_t, PLDM_TIMESTAMP104_SIZE>
@@ -208,7 +215,7 @@
     rc = decode_pldm_package_header_info(packagerHeaderInfo.data(),
                                          packagerHeaderInfo.size(),
                                          &packageHeader, &packageVersion);
-    EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
+    EXPECT_EQ(rc, PLDM_ERROR);
 }
 
 TEST(DecodePackageHeaderInfo, invalidPackageVersionStringType)
@@ -4805,3 +4812,605 @@
         &nonFunctioningComponentIndication, &nonFunctioningComponentBitmap);
     EXPECT_EQ(rc, PLDM_ERROR_INVALID_DATA);
 }
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, badArguments)
+{
+    DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    uint8_t data;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(nullptr, 0, &pin, &hdr, &iter);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), nullptr, &hdr,
+                                             &iter);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &pin, nullptr,
+                                             &iter);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &pin, &hdr,
+                                             nullptr);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, unsupportedPinVersion)
+{
+    const struct pldm_package_format_pin pin = {
+        .meta =
+            {
+                .magic = 0,
+                .version = UINT8_MAX,
+            },
+        .format =
+            {
+                .identifier = {0},
+                .revision = 0,
+            },
+    };
+
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    uint8_t data = 0;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &pin, &hdr,
+                                             &iter);
+    EXPECT_EQ(rc, -ENOTSUP);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, badPinRevision)
+{
+    const struct pldm_package_format_pin lowPin = {
+        .meta =
+            {
+                .magic = 0,
+                .version = 0,
+            },
+        .format =
+            {
+                .identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1,
+                .revision = 0,
+            },
+    };
+
+    const struct pldm_package_format_pin highPin = {
+        .meta =
+            {
+                .magic = 0,
+                .version = 0,
+            },
+        .format =
+            {
+                .identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1,
+                .revision = 3,
+            },
+    };
+
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    uint8_t data = 0;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &lowPin, &hdr,
+                                             &iter);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &highPin,
+                                             &hdr, &iter);
+    EXPECT_EQ(rc, -ENOTSUP);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, badPinMagic)
+{
+    const struct pldm_package_format_pin lowPin = {
+        .meta =
+            {
+                .magic = 0,
+                .version = 0,
+            },
+        .format =
+            {
+                .identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1,
+                .revision = 2,
+            },
+    };
+
+    const struct pldm_package_format_pin highPin = {
+        .meta =
+            {
+                .magic = UINT32_MAX,
+                .version = 0,
+            },
+        .format =
+            {
+                .identifier = PLDM_PACKAGE_HEADER_IDENTIFIER_V1_1,
+                .revision = 2,
+            },
+    };
+
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    uint8_t data = 0;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &lowPin, &hdr,
+                                             &iter);
+    EXPECT_EQ(rc, -EINVAL);
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &highPin,
+                                             &hdr, &iter);
+    EXPECT_EQ(rc, -EINVAL);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, unsupportedPinIdentifier)
+{
+    const struct pldm_package_format_pin pin = {
+        .meta =
+            {
+                .magic =
+                    LIBPLDM_SIZEAT(struct pldm__package_header_information,
+                                   package) +
+                    LIBPLDM_SIZEAT(
+                        struct pldm_package_firmware_device_id_record,
+                        firmware_device_package_data) +
+                    LIBPLDM_SIZEAT(struct pldm_descriptor, descriptor_data) +
+                    LIBPLDM_SIZEAT(
+                        struct pldm_package_downstream_device_id_record,
+                        package_data) +
+                    LIBPLDM_SIZEAT(
+                        struct pldm_package_component_image_information,
+                        component_version_string) +
+                    LIBPLDM_SIZEAT(struct pldm_package_iter, infos),
+                .version = 0,
+            },
+        .format =
+            {
+                .identifier = {0},
+                .revision = PLDM_PACKAGE_HEADER_FORMAT_REVISION_FR02H,
+            },
+    };
+
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    uint8_t data = 0;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(&data, sizeof(data), &pin, &hdr,
+                                             &iter);
+    EXPECT_EQ(rc, -ENOTSUP);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, oldConsumer)
+{
+    /* Package format revision 2 header */
+    const std::array<uint8_t, 150> package{
+        0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18, 0xa0, 0x30,
+        0xfc, 0x8a, 0x56, 0x58, 0x7d, 0x5a, 0x02, 0x94, 0x00, 0x00,
+        0xe9, 0x07, 0x03, 0x0b, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00,
+        0x76, 0x02, 0x08, 0x00, 0x01, 0x04, 't',  'e',  's',  't',
+    };
+
+    /* Package format revision 1 consumer */
+    DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR01H(pin);
+
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(package.data(), package.size(),
+                                             &pin, &hdr, &iter);
+    EXPECT_EQ(rc, -ENOTSUP);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, v1h1fd1fdd1cii)
+{
+    const std::array<uint8_t, 102> package{
+        0xf0, 0x18, 0x87, 0x8c, 0xcb, 0x7d, 0x49, 0x43, 0x98, 0x00, 0xa0,
+        0x2f, 0x05, 0x9a, 0xca, 0x02, 0x01, 0x65, 0x00, 0x00, 0xe9, 0x07,
+        0x03, 0x0b, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00, 0x76, 0x02, 0x08,
+        0x00, 0x01, 0x04, 't',  'e',  's',  't',
+
+        0x01, 0x18, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04, 0x00,
+        0x00, 0x01, 'v',  '0',  '.',  '1',  0x01, 0x00, 0x04, 0x00, 0x9c,
+        0x01, 0x00, 0x00,
+
+        0x01, 0x00, 0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00,
+        0x00, 0x01, 0x00, 0x65, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x01, 0x04, 'v',  '0',  '.',  '2',  0x00, 0x00, 0x00, 0x00,
+
+        0xb5, 0x3f, 0xf6, 0x6a,
+
+        0x5a,
+    };
+
+    struct pldm_package_downstream_device_id_record ddrec;
+    struct pldm_package_component_image_information info;
+    struct pldm_package_firmware_device_id_record fdrec;
+    DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    int nr_fdrec_desc = 0;
+    int nr_ddrec_desc = 0;
+    int nr_fdrec = 0;
+    int nr_ddrec = 0;
+    int nr_infos = 0;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(package.data(), package.size(),
+                                             &pin, &hdr, &iter);
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(memcmp(PLDM_FWUP_PACKAGE_HEADER_IDENTIFIER_V1_0.data(),
+                     hdr.package_header_identifier,
+                     PLDM_FWUP_PACKAGE_HEADER_IDENTIFIER_V1_0.size()),
+              0);
+    EXPECT_EQ(hdr.package_header_format_revision, 1);
+
+    static const std::array<uint8_t, 13> timestamp{0x00, 0xe9, 0x07, 0x03, 0x0b,
+                                                   0x16, 0x03, 0x00, 0x00, 0x00,
+                                                   0x00, 0x76, 0x02};
+    ASSERT_EQ(timestamp.size(), sizeof(hdr.package_release_date_time));
+    EXPECT_EQ(memcmp(timestamp.data(), hdr.package_release_date_time,
+                     timestamp.size()),
+              0);
+
+    EXPECT_EQ(hdr.component_bitmap_bit_length, 8);
+    EXPECT_EQ(hdr.package_version_string_type, 1);
+    ASSERT_EQ(hdr.package_version_string.length, 4);
+    EXPECT_EQ(memcmp("test", hdr.package_version_string.ptr,
+                     hdr.package_version_string.length),
+              0);
+    EXPECT_NE(hdr.areas.ptr, nullptr);
+    EXPECT_NE(hdr.areas.length, 0);
+    EXPECT_NE(hdr.package.ptr, nullptr);
+    EXPECT_NE(hdr.package.length, 0);
+
+    foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc)
+    {
+        struct pldm_descriptor desc;
+
+        EXPECT_EQ(fdrec.descriptor_count, 1);
+        EXPECT_EQ(fdrec.device_update_option_flags.value, 0);
+        EXPECT_EQ(fdrec.component_image_set_version_string_type, 1);
+        ASSERT_EQ(fdrec.component_image_set_version_string.length, 4);
+        EXPECT_EQ(memcmp("v0.1", fdrec.component_image_set_version_string.ptr,
+                         fdrec.component_image_set_version_string.length),
+                  0);
+        ASSERT_EQ(fdrec.applicable_components.bitmap.length, 1);
+        EXPECT_EQ(*fdrec.applicable_components.bitmap.ptr, 1);
+        EXPECT_NE(fdrec.record_descriptors.length, 0);
+        EXPECT_NE(fdrec.record_descriptors.ptr, nullptr);
+        ASSERT_EQ(fdrec.firmware_device_package_data.length, 0);
+
+        foreach_pldm_package_firmware_device_id_record_descriptor(iter, fdrec,
+                                                                  desc, rc)
+        {
+            static const uint8_t iana_pen_dmtf[] = {0x9c, 0x01, 0x00, 0x00};
+
+            EXPECT_EQ(desc.descriptor_type, 1);
+            ASSERT_EQ(desc.descriptor_length, sizeof(iana_pen_dmtf));
+            EXPECT_EQ(memcmp(iana_pen_dmtf, desc.descriptor_data,
+                             sizeof(iana_pen_dmtf)),
+                      0);
+
+            nr_fdrec_desc++;
+        }
+        ASSERT_EQ(rc, 0);
+
+        nr_fdrec++;
+    }
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(nr_fdrec, 1);
+    EXPECT_EQ(nr_fdrec_desc, 1);
+
+    foreach_pldm_package_downstream_device_id_record(iter, ddrec, rc)
+    {
+        struct pldm_descriptor desc;
+
+        EXPECT_EQ(ddrec.descriptor_count, 1);
+        EXPECT_EQ(ddrec.update_option_flags.value, 0);
+        EXPECT_EQ(ddrec.self_contained_activation_min_version_string_type, 1);
+        ASSERT_EQ(ddrec.self_contained_activation_min_version_string.length, 4);
+        EXPECT_EQ(
+            memcmp("v1.0",
+                   ddrec.self_contained_activation_min_version_string.ptr,
+                   ddrec.self_contained_activation_min_version_string.length),
+            0);
+        EXPECT_EQ(ddrec.self_contained_activation_min_version_comparison_stamp,
+                  0);
+        ASSERT_EQ(ddrec.applicable_components.bitmap.length, 1);
+        EXPECT_EQ(*ddrec.applicable_components.bitmap.ptr, 2);
+        EXPECT_NE(ddrec.record_descriptors.length, 0);
+        EXPECT_NE(ddrec.record_descriptors.ptr, nullptr);
+        EXPECT_EQ(ddrec.package_data.length, 0);
+
+        foreach_pldm_package_downstream_device_id_record_descriptor(iter, ddrec,
+                                                                    desc, rc)
+        {
+            static const uint8_t iana_pen_dmtf[] = {0x9c, 0x01, 0x00, 0x00};
+
+            EXPECT_EQ(desc.descriptor_type, 1);
+            ASSERT_EQ(desc.descriptor_length, sizeof(iana_pen_dmtf));
+            EXPECT_EQ(memcmp(iana_pen_dmtf, desc.descriptor_data,
+                             sizeof(iana_pen_dmtf)),
+                      0);
+
+            nr_ddrec_desc++;
+        }
+        ASSERT_EQ(rc, 0);
+
+        nr_ddrec++;
+    }
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(nr_ddrec, 0);
+    EXPECT_EQ(nr_ddrec_desc, 0);
+
+    static const pldm_package_component_image_information expected_info{
+        0x000a, 0x0000, 0xffffffff, {0}, {1}, {nullptr, 1}, 0x01, {nullptr, 0}};
+
+    foreach_pldm_package_component_image_information(iter, info, rc)
+    {
+        EXPECT_EQ(info.component_classification,
+                  expected_info.component_classification);
+        EXPECT_EQ(info.component_identifier,
+                  expected_info.component_identifier);
+        EXPECT_EQ(info.component_comparison_stamp,
+                  expected_info.component_comparison_stamp);
+        EXPECT_EQ(info.component_options.value,
+                  expected_info.component_options.value);
+        EXPECT_EQ(info.requested_component_activation_method.value,
+                  expected_info.requested_component_activation_method.value);
+        EXPECT_NE(nullptr, info.component_image.ptr);
+        EXPECT_EQ(info.component_image.length,
+                  expected_info.component_image.length);
+        EXPECT_EQ(info.component_version_string_type,
+                  expected_info.component_version_string_type);
+        ASSERT_EQ(info.component_version_string.length, 4);
+        EXPECT_EQ(memcmp("v0.2", info.component_version_string.ptr,
+                         info.component_version_string.length),
+                  0);
+
+        nr_infos++;
+    }
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(nr_infos, 1);
+}
+#endif
+
+#ifdef LIBPLDM_API_TESTING
+TEST(DecodePldmFirmwareUpdatePackage, v2h1fd1fdd1dd1ddd2cii)
+{
+    const std::array<uint8_t, 150> package{
+        0x12, 0x44, 0xd2, 0x64, 0x8d, 0x7d, 0x47, 0x18, 0xa0, 0x30,
+        0xfc, 0x8a, 0x56, 0x58, 0x7d, 0x5a, 0x02, 0x94, 0x00, 0x00,
+        0xe9, 0x07, 0x03, 0x0b, 0x16, 0x03, 0x00, 0x00, 0x00, 0x00,
+        0x76, 0x02, 0x08, 0x00, 0x01, 0x04, 't',  'e',  's',  't',
+
+        0x01, 0x18, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
+        0x00, 0x00, 0x01, 'v',  '0',  '.',  '1',  0x01, 0x00, 0x04,
+        0x00, 0x9c, 0x01, 0x00, 0x00,
+
+        0x01, 0x18, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0x01, 0x04,
+        0x00, 0x00, 0x02, 'v',  '1',  '.',  '0',  0x01, 0x00, 0x04,
+        0x00, 0x9c, 0x01, 0x00, 0x00,
+
+        0x02, 0x00,
+
+        0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+        0x01, 0x00, 0x94, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x01, 0x04, 'v',  '0',  '.',  '2',
+
+        0x0a, 0x00, 0x00, 0x00, 0xff, 0xff, 0xff, 0xff, 0x00, 0x00,
+        0x01, 0x00, 0x95, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
+        0x01, 0x04, 'v',  '2',  '.',  '0',
+
+        0xd3, 0x5c, 0x1c, 0x8a,
+
+        0x5a,
+
+        0xa5,
+    };
+    struct pldm_package_downstream_device_id_record ddrec;
+    struct pldm_package_component_image_information info;
+    struct pldm_package_firmware_device_id_record fdrec;
+    DEFINE_PLDM_PACKAGE_FORMAT_PIN_FR02H(pin);
+    pldm_package_header_information_pad hdr;
+    struct pldm_package_iter iter;
+    int nr_fdrec_desc = 0;
+    int nr_ddrec_desc = 0;
+    int nr_fdrec = 0;
+    int nr_ddrec = 0;
+    int nr_infos = 0;
+    int rc;
+
+    rc = decode_pldm_firmware_update_package(package.data(), package.size(),
+                                             &pin, &hdr, &iter);
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(memcmp(PLDM_FWUP_PACKAGE_HEADER_IDENTIFIER_V1_1.data(),
+                     hdr.package_header_identifier,
+                     PLDM_FWUP_PACKAGE_HEADER_IDENTIFIER_V1_1.size()),
+              0);
+    EXPECT_EQ(hdr.package_header_format_revision, 2);
+
+    static const std::array<uint8_t, 13> timestamp{0x00, 0xe9, 0x07, 0x03, 0x0b,
+                                                   0x16, 0x03, 0x00, 0x00, 0x00,
+                                                   0x00, 0x76, 0x02};
+    ASSERT_EQ(timestamp.size(), sizeof(hdr.package_release_date_time));
+    EXPECT_EQ(memcmp(timestamp.data(), hdr.package_release_date_time,
+                     timestamp.size()),
+              0);
+
+    EXPECT_EQ(hdr.component_bitmap_bit_length, 8);
+    EXPECT_EQ(hdr.package_version_string_type, 1);
+    ASSERT_EQ(hdr.package_version_string.length, 4);
+    EXPECT_EQ(memcmp("test", hdr.package_version_string.ptr,
+                     hdr.package_version_string.length),
+              0);
+    EXPECT_NE(hdr.areas.ptr, nullptr);
+    EXPECT_NE(hdr.areas.length, 0);
+    EXPECT_NE(hdr.package.ptr, nullptr);
+    EXPECT_NE(hdr.package.length, 0);
+
+    foreach_pldm_package_firmware_device_id_record(iter, fdrec, rc)
+    {
+        struct pldm_descriptor desc;
+
+        EXPECT_EQ(fdrec.descriptor_count, 1);
+        EXPECT_EQ(fdrec.device_update_option_flags.value, 0);
+        EXPECT_EQ(fdrec.component_image_set_version_string_type, 1);
+        ASSERT_EQ(fdrec.component_image_set_version_string.length, 4);
+        EXPECT_EQ(memcmp("v0.1", fdrec.component_image_set_version_string.ptr,
+                         fdrec.component_image_set_version_string.length),
+                  0);
+        ASSERT_EQ(fdrec.applicable_components.bitmap.length, 1);
+        EXPECT_EQ(*fdrec.applicable_components.bitmap.ptr, 1);
+        EXPECT_NE(fdrec.record_descriptors.length, 0);
+        EXPECT_NE(fdrec.record_descriptors.ptr, nullptr);
+        ASSERT_EQ(fdrec.firmware_device_package_data.length, 0);
+
+        foreach_pldm_package_firmware_device_id_record_descriptor(iter, fdrec,
+                                                                  desc, rc)
+        {
+            static const uint8_t iana_pen_dmtf[] = {0x9c, 0x01, 0x00, 0x00};
+
+            EXPECT_EQ(desc.descriptor_type, 1);
+            ASSERT_EQ(desc.descriptor_length, sizeof(iana_pen_dmtf));
+            EXPECT_EQ(memcmp(iana_pen_dmtf, desc.descriptor_data,
+                             sizeof(iana_pen_dmtf)),
+                      0);
+
+            nr_fdrec_desc++;
+        }
+        ASSERT_EQ(rc, 0);
+
+        nr_fdrec++;
+    }
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(nr_fdrec, 1);
+    EXPECT_EQ(nr_fdrec_desc, 1);
+
+    foreach_pldm_package_downstream_device_id_record(iter, ddrec, rc)
+    {
+        struct pldm_descriptor desc;
+
+        EXPECT_EQ(ddrec.descriptor_count, 1);
+        EXPECT_EQ(ddrec.update_option_flags.value, 0);
+        EXPECT_EQ(ddrec.self_contained_activation_min_version_string_type, 1);
+        ASSERT_EQ(ddrec.self_contained_activation_min_version_string.length, 4);
+        EXPECT_EQ(
+            memcmp("v1.0",
+                   ddrec.self_contained_activation_min_version_string.ptr,
+                   ddrec.self_contained_activation_min_version_string.length),
+            0);
+        EXPECT_EQ(ddrec.self_contained_activation_min_version_comparison_stamp,
+                  0);
+        ASSERT_EQ(ddrec.applicable_components.bitmap.length, 1);
+        EXPECT_EQ(*ddrec.applicable_components.bitmap.ptr, 2);
+        EXPECT_NE(ddrec.record_descriptors.length, 0);
+        EXPECT_NE(ddrec.record_descriptors.ptr, nullptr);
+        EXPECT_EQ(ddrec.package_data.length, 0);
+
+        foreach_pldm_package_downstream_device_id_record_descriptor(iter, ddrec,
+                                                                    desc, rc)
+        {
+            static const uint8_t iana_pen_dmtf[] = {0x9c, 0x01, 0x00, 0x00};
+
+            EXPECT_EQ(desc.descriptor_type, 1);
+            ASSERT_EQ(desc.descriptor_length, sizeof(iana_pen_dmtf));
+            EXPECT_EQ(memcmp(iana_pen_dmtf, desc.descriptor_data,
+                             sizeof(iana_pen_dmtf)),
+                      0);
+
+            nr_ddrec_desc++;
+        }
+        ASSERT_EQ(rc, 0);
+
+        nr_ddrec++;
+    }
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(nr_ddrec, 1);
+    EXPECT_EQ(nr_ddrec_desc, 1);
+
+    static const std::array<const char*, 2> component_versions = {
+        "v0.2",
+        "v2.0",
+    };
+    static const std::array<pldm_package_component_image_information, 2>
+        expected_infos{{{0x000a,
+                         0x0000,
+                         0xffffffff,
+                         {0},
+                         {1},
+                         {nullptr, 1},
+                         0x01,
+                         {nullptr, 0}},
+                        {0x000a,
+                         0x0000,
+                         0xffffffff,
+                         {0},
+                         {1},
+                         {nullptr, 1},
+                         0x01,
+                         {nullptr, 0}}}};
+    static const std::array<uint8_t, 2> expected_images{0x5a, 0xa5};
+
+    foreach_pldm_package_component_image_information(iter, info, rc)
+    {
+        const struct pldm_package_component_image_information* expected;
+        const char* version;
+        uint8_t image;
+
+        expected = &expected_infos.at(nr_infos);
+        version = component_versions.at(nr_infos);
+        image = expected_images.at(nr_infos);
+
+        EXPECT_EQ(info.component_classification,
+                  expected->component_classification);
+        EXPECT_EQ(info.component_identifier, expected->component_identifier);
+        EXPECT_EQ(info.component_comparison_stamp,
+                  expected->component_comparison_stamp);
+        EXPECT_EQ(info.component_options.value,
+                  expected->component_options.value);
+        EXPECT_EQ(info.requested_component_activation_method.value,
+                  expected->requested_component_activation_method.value);
+        EXPECT_NE(info.component_image.ptr, expected->component_image.ptr);
+        EXPECT_EQ(info.component_image.length,
+                  expected->component_image.length);
+        EXPECT_EQ(*info.component_image.ptr, image);
+        EXPECT_EQ(info.component_version_string_type,
+                  expected->component_version_string_type);
+        ASSERT_EQ(info.component_version_string.length, 4);
+        EXPECT_EQ(memcmp(version, info.component_version_string.ptr,
+                         info.component_version_string.length),
+                  0);
+
+        nr_infos++;
+    }
+    ASSERT_EQ(rc, 0);
+
+    EXPECT_EQ(nr_infos, 2);
+}
+#endif