fw update: pldm package parser

The code is taken from 'pldm' repo, but there is an ongoing effort to
make it part of libpldm [1].

The intent of this patch is to provide a few high-level functions to
parse a PLDM fw update package.

If/when the package parser is available in libpldm, this code can be
dropped.

References:
- [1] https://gerrit.openbmc.org/c/openbmc/libpldm/+/77095

Tested: next patch in series

Change-Id: I8212d88702e59d9d78965baf4e8c2243b8035b42
Signed-off-by: Alexander Hansen <alexander.hansen@9elements.com>
diff --git a/common/pldm/package_parser.hpp b/common/pldm/package_parser.hpp
new file mode 100644
index 0000000..67f9b95
--- /dev/null
+++ b/common/pldm/package_parser.hpp
@@ -0,0 +1,192 @@
+#pragma once
+
+#include "types.hpp"
+
+#include <libpldm/firmware_update.h>
+
+#include <array>
+#include <cstdint>
+#include <memory>
+#include <tuple>
+#include <vector>
+
+// NOLINTBEGIN
+
+namespace pldm
+{
+
+namespace fw_update
+{
+
+/** @class PackageParser
+ *
+ *  PackageParser is the abstract base class for parsing the PLDM firmware
+ *  update package. The PLDM firmware update contains two major sections; the
+ *  firmware package header, and the firmware package payload. Each package
+ *  header version will have a concrete implementation of the PackageParser.
+ *  The concrete implementation understands the format of the package header and
+ *  will implement the parse API.
+ */
+class PackageParser
+{
+  public:
+    PackageParser() = delete;
+    PackageParser(const PackageParser&) = delete;
+    PackageParser(PackageParser&&) = default;
+    PackageParser& operator=(const PackageParser&) = delete;
+    PackageParser& operator=(PackageParser&&) = delete;
+    virtual ~PackageParser() = default;
+
+    /** @brief Constructor
+     *
+     *  @param[in] pkgHeaderSize - Size of package header section
+     *  @param[in] pkgVersion - Package version
+     *  @param[in] componentBitmapBitLength - The number of bits used to
+     *                                        represent the bitmap in the
+     *                                        ApplicableComponents field for a
+     *                                        matching device.
+     */
+    explicit PackageParser(PackageHeaderSize pkgHeaderSize,
+                           const PackageVersion& pkgVersion,
+                           ComponentBitmapBitLength componentBitmapBitLength) :
+        pkgHeaderSize(pkgHeaderSize), pkgVersion(pkgVersion),
+        componentBitmapBitLength(componentBitmapBitLength)
+    {}
+
+    /** @brief Parse the firmware update package header
+     *
+     *  @param[in] pkgHdr - Package header
+     *  @param[in] pkgSize - Size of the firmware update package
+     *
+     *  @note Throws exception is parsing fails
+     */
+    virtual void parse(const std::vector<uint8_t>& pkgHdr,
+                       uintmax_t pkgSize) = 0;
+
+    /** @brief Get firmware device ID records from the package
+     *
+     *  @return if parsing the package is successful, return firmware device ID
+     *          records
+     */
+    const FirmwareDeviceIDRecords& getFwDeviceIDRecords() const
+    {
+        return fwDeviceIDRecords;
+    }
+
+    /** @brief Get component image information from the package
+     *
+     *  @return if parsing the package is successful, return component image
+     *          information
+     */
+    const ComponentImageInfos& getComponentImageInfos() const
+    {
+        return componentImageInfos;
+    }
+
+    /** @brief Device identifiers of the managed FDs */
+    const PackageHeaderSize pkgHeaderSize;
+
+    /** @brief Package version string */
+    const PackageVersion pkgVersion;
+
+  protected:
+    /** @brief Parse the firmware device identification area
+     *
+     *  @param[in] deviceIdRecCount - count of firmware device ID records
+     *  @param[in] pkgHdr - firmware package header
+     *  @param[in] offset - offset in package header which is the start of the
+     *                      firmware device identification area
+     *
+     *  @return On success return the offset which is the end of the firmware
+     *          device identification area, on error throw exception.
+     */
+    size_t parseFDIdentificationArea(DeviceIDRecordCount deviceIdRecCount,
+                                     const std::vector<uint8_t>& pkgHdr,
+                                     size_t offset);
+
+    /** @brief Parse the component image information area
+     *
+     *  @param[in] compImageCount - component image count
+     *  @param[in] pkgHdr - firmware package header
+     *  @param[in] offset - offset in package header which is the start of the
+     *                      component image information area
+     *
+     *  @return On success return the offset which is the end of the component
+     *          image information area, on error throw exception.
+     */
+    size_t parseCompImageInfoArea(ComponentImageCount compImageCount,
+                                  const std::vector<uint8_t>& pkgHdr,
+                                  size_t offset);
+
+    /** @brief Validate the total size of the package
+     *
+     *  Verify the total size of the package is the sum of package header and
+     *  the size of each component.
+     *
+     *  @param[in] pkgSize - firmware update package size
+     *
+     *  @note Throws exception if validation fails
+     */
+    void validatePkgTotalSize(uintmax_t pkgSize);
+
+    /** @brief Firmware Device ID Records in the package */
+    FirmwareDeviceIDRecords fwDeviceIDRecords;
+
+    /** @brief Component Image Information in the package */
+    ComponentImageInfos componentImageInfos;
+
+    /** @brief The number of bits that will be used to represent the bitmap in
+     *         the ApplicableComponents field for matching device. The value
+     *         shall be a multiple of 8 and be large enough to contain a bit
+     *         for each component in the package.
+     */
+    const ComponentBitmapBitLength componentBitmapBitLength;
+};
+
+/** @class PackageParserV1
+ *
+ *  This class implements the package parser for the header format version 0x01
+ */
+class PackageParserV1 final : public PackageParser
+{
+  public:
+    PackageParserV1() = delete;
+    PackageParserV1(const PackageParserV1&) = delete;
+    PackageParserV1(PackageParserV1&&) = default;
+    PackageParserV1& operator=(const PackageParserV1&) = delete;
+    PackageParserV1& operator=(PackageParserV1&&) = delete;
+    ~PackageParserV1() = default;
+
+    /** @brief Constructor
+     *
+     *  @param[in] pkgHeaderSize - Size of package header section
+     *  @param[in] pkgVersion - Package version
+     *  @param[in] componentBitmapBitLength - The number of bits used to
+     *                                        represent the bitmap in the
+     *                                        ApplicableComponents field for a
+     *                                        matching device.
+     */
+    explicit PackageParserV1(
+        PackageHeaderSize pkgHeaderSize, const PackageVersion& pkgVersion,
+        ComponentBitmapBitLength componentBitmapBitLength) :
+        PackageParser(pkgHeaderSize, pkgVersion, componentBitmapBitLength)
+    {}
+
+    virtual void parse(const std::vector<uint8_t>& pkgHdr, uintmax_t pkgSize);
+};
+
+/** @brief Parse the package header information
+ *
+ *  @param[in] pkgHdrInfo - package header information section in the package
+ *
+ *  @return On success return the PackageParser for the header format version
+ *          on failure return nullptr
+ */
+std::unique_ptr<PackageParser>
+    parsePackageHeader(std::vector<uint8_t>& pkgHdrInfo);
+
+} // namespace fw_update
+
+} // namespace pldm
+
+// NOLINTEND