/*
 * Copyright (c) 2008-2015 Freescale Semiconductor, Inc.
 * Copyright 2016-2018 NXP
 * All rights reserved.
 *
 * SPDX-License-Identifier: BSD-3-Clause
 */
#if !defined(_EncoreBootImage_h_)
#define _EncoreBootImage_h_

#include <list>
#include <vector>
#include <string>
#include <iostream>
#include <fstream>
#include <string.h>
#include "BootImage.h"
#include "rijndael.h"
#include "smart_ptr.h"
#include "AESKey.h"
#include "StExecutableImage.h"

namespace elftosb
{
//! An AES-128 cipher block is 16 bytes.
typedef uint8_t cipher_block_t[16];

//! A SHA-1 digest is 160 bits, or 20 bytes.
typedef uint8_t sha1_digest_t[20];

//! Unique identifier type for a section.
typedef uint32_t section_id_t;

//! Utility to return the byte length of a number of cipher blocks.
inline size_t sizeOfCipherBlocks(unsigned count)
{
    return sizeof(cipher_block_t) * count;
}

//! Utility to return the number of cipher blocks required to hold an object
//! that is \a s bytes long.
inline size_t numberOfCipherBlocks(size_t s)
{
    return (s + sizeof(cipher_block_t) - 1) / sizeof(cipher_block_t);
}

//! Utility to calculate the byte length for the cipher blocks required to hold
//! and object that is \a bytes long.
inline size_t sizeInCipherBlocks(size_t s)
{
    return (unsigned)sizeOfCipherBlocks(numberOfCipherBlocks(s));
}

//! Utility to return the number of bytes of padding required to fill out
//! the last cipher block in a set of cipher blocks large enough to hold
//! an object that is \a s bytes large. The result may be 0 if \a s is
//! an even multiple of the cipher block size.
inline size_t sizeOfPaddingForCipherBlocks(size_t s)
{
    return sizeInCipherBlocks(s) - s;
}

/*!
 * \brief Class to manage Encore boot image files.
 *
 * Initially this class will only support generation of boot images, but
 * its design will facilitate the addition of the ability to read an
 * image and examine its contents.
 *
 * A boot image is composed of the following regions:
 * - Header
 * - Section table
 * - Key dictionary
 * - Section data
 * - Authentication
 *
 * Multiple sections are within a boot image are fully supported. Two general types
 * of sections are supported with this class. Bootable sections, represented by the
 * EncoreBootImage::BootSection class, contain a sequence of commands to be
 * interpreted by the boot ROM. Data sections are represented by the
 * EncoreBootImage::DataSection class and can contain any arbitrary data.
 *
 * An image can either be encrypted or unencrypted. The image uses a session key,
 * or DEK (data encryption key), and the key dictionary to support any number of keys
 * using a single image. The header and section table are always unencrypted even
 * in encrypted images. This allows host utilities to access the individual
 * sections without needing to have access to an encryption key.
 *
 * To construct a boot image, first create an instance of EncoreBootImage. Then
 * create instances of the EncoreBootImage::BootSection or EncoreBootImage::DataSection
 * for each of the sections in the image. For bootable sections, create and add
 * the desired boot command objects. These are all subclasses of
 * EncoreBootImage::BootCommand.
 *
 * If the boot image is to be encrypted, you need to add keys, which are instances
 * of the AES128Key class. If no keys are added, the entire boot image will be unencrypted.
 *
 * When the image is fully constructed, it can be written to any std::ostream with
 * a call to writeToStream(). The same image can be written to streams any
 * number of times.
 */
class EncoreBootImage : public BootImage
{
public:
    //! \brief Flag constants for the m_flags field of #elftosb::EncoreBootImage::boot_image_header_t.
    enum
    {
        ROM_DISPLAY_PROGRESS = (1 << 0), //!< Print progress reports.
        ROM_VERBOSE_PROGRESS = (1 << 1)  //!< Progress reports are verbose.
    };

    enum
    {
        ROM_IMAGE_HEADER_SIGNATURE =
            'STMP', //!< Signature in #elftosb::EncoreBootImage::boot_image_header_t::m_signature.
        ROM_IMAGE_HEADER_SIGNATURE2 =
            'sgtl',                       //!< Value for #elftosb::EncoreBootImage::boot_image_header_t::m_signature2;
        ROM_BOOT_IMAGE_MAJOR_VERSION = 1, //!< Current boot image major version.
        ROM_BOOT_IMAGE_MINOR_VERSION = 2  //!< Current boot image minor version.
    };

    enum
    {
        //! Minimum alignment for a section is 16 bytes.
        BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT = sizeof(cipher_block_t)
    };

// All of these structures are packed to byte alignment in order to match
// the structure on disk.
#pragma pack(1)

    //! \brief Header for the entire boot image.
    //!
    //! Fields of this header are arranged so that those used by the bootloader ROM all come
    //! first. They are also set up so that all fields are not split across cipher block
    //! boundaries. The fields not used by the bootloader are not subject to this
    //! restraint.
    //!
    //! Image header size is always a round number of cipher blocks. The same also applies to
    //! the boot image itself. The padding, held in #elftosb::EncoreBootImage::boot_image_header_t::m_padding0
    //! and #elftosb::EncoreBootImage::boot_image_header_t::m_padding1 is filled with random bytes.
    //!
    //! The DEK dictionary, section table, and each section data region must all start on
    //! cipher block boundaries.
    //!
    //! This header is not encrypted in the image file.
    //!
    //! The m_digest field contains a SHA-1 digest of the fields of the header that follow it.
    //! It is the first field in the header so it doesn't change position or split the header
    //! in two if fields are added to the header.
    struct boot_image_header_t
    {
        union
        {
            sha1_digest_t m_digest; //!< SHA-1 digest of image header. Also used as the crypto IV.
            struct
            {
                cipher_block_t m_iv; //!< The first 16 bytes of the digest form the initialization vector.
                uint8_t m_extra[4];  //!< The leftover top four bytes of the SHA-1 digest.
            };
        };
        uint8_t m_signature[4];       //!< 'STMP', see #ROM_IMAGE_HEADER_SIGNATURE.
        uint8_t m_majorVersion;       //!< Major version for the image format, see #ROM_BOOT_IMAGE_MAJOR_VERSION.
        uint8_t m_minorVersion;       //!< Minor version of the boot image format, see #ROM_BOOT_IMAGE_MINOR_VERSION.
        uint16_t m_flags;             //!< Flags or options associated with the entire image.
        uint32_t m_imageBlocks;       //!< Size of entire image in blocks.
        uint32_t m_firstBootTagBlock; //!< Offset from start of file to the first boot tag, in blocks.
        section_id_t m_firstBootableSectionID; //!< ID of section to start booting from.
        uint16_t m_keyCount;                   //!< Number of entries in DEK dictionary.
        uint16_t m_keyDictionaryBlock;         //!< Starting block number for the key dictionary.
        uint16_t m_headerBlocks;               //!< Size of this header, including this size word, in blocks.
        uint16_t m_sectionCount;               //!< Number of section headers in this table.
        uint16_t m_sectionHeaderSize;          //!< Size in blocks of a section header.
        uint8_t m_padding0[2];                 //!< Padding to align #m_timestamp to long word.
        uint8_t m_signature2[4];      //!< Second signature to distinguish this .sb format from the 36xx format, see
                                      //!#ROM_IMAGE_HEADER_SIGNATURE2.
        uint64_t m_timestamp;         //!< Timestamp when image was generated in microseconds since 1-1-2000.
        version_t m_productVersion;   //!< Product version.
        version_t m_componentVersion; //!< Component version.
        uint16_t m_driveTag;          //!< Drive tag for the system drive which this boot image belongs to.
        uint8_t m_padding1[6];        //!< Padding to round up to next cipher block.
    };

    //! \brief Entry in #elftosb::EncoreBootImage::dek_dictionary_t.
    //!
    //! The m_dek field in each entry is encrypted using the KEK with the m_iv field from
    //! the image header as the IV.
    struct dek_dictionary_entry_t
    {
        cipher_block_t m_mac; //!< CBC-MAC of the header.
        aes128_key_t m_dek;   //!< AES-128 key with which the image payload is encrypted.
    };

    //! \brief The DEK dictionary always follows the image header, in the next cipher block.
    struct dek_dictionary_t
    {
        dek_dictionary_entry_t m_entries[1];
    };

    //! \brief Section flags constants for the m_flags field of #elftosb::EncoreBootImage::section_header_t.
    enum
    {
        ROM_SECTION_BOOTABLE = (1 << 0), //!< The section contains bootloader commands.
        ROM_SECTION_CLEARTEXT =
            (1 << 1) //!< The section is unencrypted. Applies only if the rest of the boot image is encrypted.
    };

    //! \brief Information about each section, held in the section table.
    //! \see section_table_t
    struct section_header_t
    {
        uint32_t m_tag;    //!< Unique identifier for this section. High bit must be zero.
        uint32_t m_offset; //!< Offset to section data from start of image in blocks.
        uint32_t m_length; //!< Size of section data in blocks.
        uint32_t m_flags;  //!< Section flags.
    };

    //! \brief An index of all sections within the boot image.
    //!
    //! The section table will be padded so that its length is divisible by 16 (if necessary).
    //! Actually, each entry is padded to be a round number of cipher blocks, which
    //! automatically makes this true for the entire table.
    //!
    //! Sections are ordered as they appear in this table, but are identified by the
    //! #elftosb::EncoreBootImage::section_header_t::m_tag.
    //!
    //! The data for each section in encrypted separately with the DEK in CBC mode using
    //! m_iv for the IV. This allows the ROM to jump to any given section without needing
    //! to read the previous cipher block. In addition, the data for each section is
    //! prefixed with a "boot tag", which describes the section which follows it. Boot
    //! tags are the same format as a boot command, and are described by the
    //! EncoreBootImage::TagCommand class.
    //!
    //! The section table starts immediately after the image header, coming before the
    //! key dictionary (if present). The section table is not encrypted.
    struct section_table_t
    {
        section_header_t m_sections[1]; //!< The table entries.
    };

    //! \brief Structure for a Piano bootloader command.
    //!
    //! Each command is composed of a fixed length header of 16 bytes. This happens to be
    //! the size of a cipher block. Most commands will only require the header.
    //!
    //! But some commands, i.e. the "load data" command, may require additional arbitrary
    //! amounts of data. This data is packed into the N cipher blocks that immediately
    //! follow the command header. If the length of the data is not divisible by 16, then
    //! random (not zero!) pad bytes will be added.
    struct boot_command_t
    {
        uint8_t m_checksum; //!< Simple checksum over other command fields.
        uint8_t m_tag;      //!< Tag telling which command this is.
        uint16_t m_flags;   //!< Flags for this command.
        uint32_t m_address; //!< Target address.
        uint32_t m_count;   //!< Number of bytes on which to operate.
        uint32_t m_data;    //!< Additional data used by certain commands.
    };

#pragma pack()

    //! \brief Bootloader command tag constants.
    enum
    {
        ROM_NOP_CMD = 0x00,        //!< A no-op command.
        ROM_TAG_CMD = 0x01,        //!< Section tag command.
        ROM_LOAD_CMD = 0x02,       //!< Load data command.
        ROM_FILL_CMD = 0x03,       //!< Pattern fill command.
        ROM_JUMP_CMD = 0x04,       //!< Jump to address command.
        ROM_CALL_CMD = 0x05,       //!< Call function command.
        ROM_MODE_CMD = 0x06,       //!< Change boot mode command.
        ROM_ERASE_CMD = 0x07,      //!< Flash erase command.
        ROM_RESET_CMD = 0x08,      //!< Reset command.
        ROM_MEM_ENABLE_CMD = 0x09, //!< Memory Enable command.
        ROM_PROG_CMD = 0x0a        //!< Program persistent bits command.
    };

    //! \brief Flag field constants for #ROM_TAG_CMD.
    enum
    {
        ROM_LAST_TAG = (1 << 0) //!< This tag command is the last one in the image.
    };

    //! \brief Flag field constants for #ROM_LOAD_CMD.
    enum
    {
        ROM_LOAD_DCD = (1 << 0) //!< Execute the DCD after loading completes.
    };

    //! \brief Flag field constants for #ROM_FILL_CMD.
    enum
    {
        ROM_FILL_BYTE = 0,      //!< Fill with byte sized pattern.
        ROM_FILL_HALF_WORD = 1, //!< Fill with half-word sized pattern.
        ROM_FILL_WORD = 2       //!< Fill with word sized pattern.
    };

    //! \brief Flag field constants for #ROM_JUMP_CMD and #ROM_CALL_CMD.
    enum
    {
        ROM_HAB_EXEC = (1 << 0),    //!< Changes jump or call command to a HAB jump or call.
        ROM_JUMP_SP_MASK = (1 << 1) //!< Initial stack pointer is specified for jump command.
    };

    //! \brief Flags for the #ROM_ERASE_CMD.
    enum
    {
        ROM_ERASE_ALL_MASK = (1 << 0),          //!< Erase all flash instead of just a range.
        ROM_ERASE_ALL_UNSECURE_MASK = (1 << 1), //!< Erase all unsecure (available on some Kinetis parts).
    };

    //! \brief Flags for the #ROM_PROG_CMD.
    enum
    {
        ROM_PROG_8BYTE_MASK = (1 << 0) //!< Program 8 bytes instead of 4 bytes.
    };

    //! \brief Memory ID flags field for ROM CMDs
    enum
    {
        ROM_MEM_DEVICE_ID_MASK = 0xff00, //!< Bit mask for mem controller ID.
        ROM_MEM_DEVICE_ID_SHIFT = 8,   //!< Bit position of mem controller ID.

        ROM_MEM_GROUP_ID_MASK = 0xf0,
        ROM_MEM_GROUP_ID_SHIFT = 4,
    };

//! @brief Bit mask for device ID.
#define DEVICE_ID_MASK 0xff
//! @brief Bit position of device ID.
#define DEVICE_ID_SHIFT 0
//! @brief Bit mask for group ID.
#define GROUP_ID_MASK 0xf00
//! @brief Bit position of group ID.
#define GROUP_ID_SHIFT 8

/*! @brief Construct a memory ID from a given group ID and device ID. */
#define MAKE_MEMORYID(group, device) ((((group) << GROUP_ID_SHIFT) & GROUP_ID_MASK) \
                                     | (((device) << DEVICE_ID_SHIFT) & DEVICE_ID_MASK))
/*! @brief Get group ID from a given memory ID. */
#define GROUPID(memoryId) (((memoryId) & GROUP_ID_MASK) >> GROUP_ID_SHIFT)

/*! @brief Get device ID from a given memory ID. */
#define DEVICEID(memoryId) (((memoryId) & DEVICE_ID_MASK ) >> DEVICE_ID_SHIFT)

    //! \brief Constants for Memory ID.
    enum
    {
        ROM_MEM_ID_Internal = 0, //!< Internal memoryID
        ROM_MEM_ID_QSPI = 1, //!< External QuadSpi controller.
        ROM_MEM_ID_IFR_FUSE = 4, //!< IFR or eFUSE memory.
        ROM_MEM_ID_SEMCNOR = 8, //!< SEMC NOR FLASH.
        ROM_MEM_ID_FLEXSPINOR = 9, // FlexSPI NOR FLASH.
        ROM_MEM_ID_FLASHEXECUTEONLY = 0x10, // Execute-only region on internal flash.

        ROM_MEM_ID_SEMCNAND = 0x100, // SEMC NAND FLASH.
        ROM_MEM_ID_SPINAND = 0x101, // SPI NAND memory.
        ROM_MEM_ID_SPIEEPROM = 0x110, // SPI EEPROM memory.
        ROM_MEM_ID_I2CEEPROM = 0x111, // I2C EEPROM memory.
        ROM_MEM_ID_SD = 0x120, // uSDHC SD Card memory.
        ROM_MEM_ID_MMC = 0x121, // uSDHC MMC Card memory.
    };

    //! \brief Memory group definition
    enum 
    {
        ROM_MEM_GROUP_INTERNAL = 0, //!<  Kinetis internal 4G memory region.
        ROM_MEM_GROUP_EXTERNAL = 1, //!<  Kinetis external memory region.
    };

public:
    // Forward declaration.
    class Section;

    /*!
     * \brief Base class for objects that produce cipher blocks.
     */
    class CipherBlockGenerator
    {
    public:
        //! \name Cipher blocks
        //@{
        //! \brief Returns the total number of cipher blocks.
        //!
        //! The default implementation returns 0, indicating that no blocks are
        //! available.
        virtual unsigned getBlockCount() const { return 0; }
        //! \brief Returns the contents of up to \a maxCount cipher blocks.
        //!
        //! Up to \a maxCount cipher blocks are copied into the buffer pointed to by
        //! the \a data argument. This is only a request for \a maxCount blocks,
        //! the subclass implementation of this method is free to return any number
        //! of blocks from 0 up to \a maxCount. A return value of 0 indicates that
        //! no more blocks are available. The index of the first block to copy is
        //! held in the \a offset argument.
        //!
        //! \param offset Starting block number to copy. Zero means the first available block.
        //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater.
        //! \param data Buffer for outgoing cipher blocks. Must have enough room to hold
        //!		\a maxCount blocks.
        //!
        //! \return The number of cipher blocks copied into \a data.
        //! \retval 0 No more blocks are available and nothing was written to \a data.
        virtual unsigned getBlocks(unsigned offset, unsigned maxCount, cipher_block_t *data) { return 0; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const {}
    };

    /*!
     * \brief Abstract base class for all bootloader commands.
     */
    class BootCommand : public CipherBlockGenerator
    {
    public:
        //! \brief Creates the correct subclass of BootCommand for the given raw data.
        static BootCommand *createFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

    public:
        //! \brief Default constructor.
        BootCommand()
            : CipherBlockGenerator()
        {
        }

        //! \brief Destructor.
        virtual ~BootCommand() {}
        //! \brief Read the command contents from raw data.
        //!
        //! The subclass implementations should validate the contents of the command, including
        //! the fields of the command header in the first block. It should be assumed that
        //! only the tag field was examined to determine which subclass of BootCommand
        //! should be created.
        //!
        //! \param blocks Pointer to the raw data blocks.
        //! \param count Number of blocks pointed to by \a blocks.
        //! \param[out] consumed On exit, this points to the number of cipher blocks that were occupied
        //!		by the command. Should be at least 1 for every command. This must not be NULL
        //!		on entry!
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed) = 0;

        //! \name Header
        //@{
        //! \brief Pure virtual method to return the tag value for this command.
        virtual uint8_t getTag() const = 0;

        //! \brief Pure virtual method to construct the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header) = 0;

        //! \brief Calculates the checksum for the given command header.
        virtual uint8_t calculateChecksum(const boot_command_t &header);
        //@}

        //! \name Cipher blocks
        //@{
        //! \brief Returns the total number of cipher blocks.
        virtual unsigned getBlockCount() const;

        //! \brief Returns the contents of up to \a maxCount cipher blocks.
        virtual unsigned getBlocks(unsigned offset, unsigned maxCount, cipher_block_t *data);
        //@}

        //! \name Data blocks
        //@{
        //! \brief Returns the number of data cipher blocks that follow this command.
        //!
        //! The default implementation returns 0, indicating that no data blocks are
        //! available.
        virtual unsigned getDataBlockCount() const { return 0; }
        //! \brief Returns the contents of up to \a maxCount data blocks.
        //!
        //! Up to \a maxCount data blocks are copied into the buffer pointed to by
        //! the \a data argument. This is only a request for \a maxCount blocks,
        //! the subclass implementation of this method is free to return any number
        //! of blocks from 0 up to \a maxCount. A return value of 0 indicates that
        //! no more blocks are available. The index of the first block to copy is
        //! held in the \a offset argument.
        //!
        //! \param offset Starting block number to copy. Zero means the first available block.
        //! \param maxCount Up to this number of blocks may be copied into \a data. Must be 1 or greater.
        //! \param data Buffer for outgoing data blocks. Must have enough room to hold
        //!		\a maxCount blocks.
        //!
        //! \return The number of data blocks copied into \a data.
        //! \retval 0 No more blocks are available and nothing was written to \a data.
        virtual unsigned getDataBlocks(unsigned offset, unsigned maxCount, cipher_block_t *data) { return 0; }
        //@}

    protected:
        //! The flag bit values for the \a whichFields parameter of validateHeader().
        enum
        {
            CMD_TAG_FIELD = 1,
            CMD_FLAGS_FIELD = 2,
            CMD_ADDRESS_FIELD = 4,
            CMD_COUNT_FIELD = 8,
            CMD_DATA_FIELD = 16
        };

        //! \brief
        void validateHeader(const boot_command_t *modelHeader, const boot_command_t *testHeader, unsigned whichFields);
    };

    /*!
     * \brief No operation bootloader command.
     */
    class NopCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        NopCommand()
            : BootCommand()
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_NOP_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;
    };

    /*!
     * \brief Section tag bootloader command.
     */
    class TagCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        TagCommand()
            : BootCommand()
        {
        }

        //! \brief Constructor taking a section object.
        TagCommand(const Section &section);

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_TAG_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Field accessors
        //@{
        inline void setSectionIdentifier(uint32_t identifier) { m_sectionIdentifier = identifier; }
        inline void setSectionLength(uint32_t length) { m_sectionLength = length; }
        inline void setSectionFlags(uint32_t flags) { m_sectionFlags = flags; }
        inline void setLast(bool isLast) { m_isLast = isLast; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        uint32_t m_sectionIdentifier; //!< Unique identifier for the section containing this command.
        uint32_t m_sectionLength;     //!< Number of cipher blocks this section occupies.
        uint32_t m_sectionFlags;      //!< Flags pertaining to this section.
        bool m_isLast;                //!< Is this the last tag command?
    };

    /*!
     * \brief Load data bootloader command.
     */
    class LoadCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        LoadCommand();

        //! \brief Constructor.
        LoadCommand(uint32_t address, const uint8_t *data, uint32_t length);

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_LOAD_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);

        //! \brief Sets the load-dcd flag.
        inline void setDCD(bool isDCD) { m_loadDCD = isDCD; }
        //@}

        //! \name Address
        //@{
        inline void setLoadAddress(uint32_t address) { m_address = address; }
        inline uint32_t getLoadAddress() const { return m_address; }
        //@}

        //! \name Load data
        //@{
        //! \brief Set the data for the command to load.
        void setData(const uint8_t *data, uint32_t length);

        inline uint8_t *getData() { return m_data; }
        inline const uint8_t *getData() const { return m_data; }
        inline uint32_t getLength() const { return m_length; }
        //@}

        //! \name Data blocks
        //@{
        //! \brief Returns the number of data cipher blocks that follow this command.
        virtual unsigned getDataBlockCount() const;

        //! \brief Returns the contents of up to \a maxCount data blocks.
        virtual unsigned getDataBlocks(unsigned offset, unsigned maxCount, cipher_block_t *data);
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

        //! \name Memory space id.
        //@{
        void setMemoryId(uint32_t id) { m_memoryId = id; }
        uint32_t getMemoryId() const { return m_memoryId; }
        //@}

    protected:
        smart_array_ptr<uint8_t> m_data; //!< Pointer to data to load.
        uint8_t m_padding[15];           //!< Up to 15 pad bytes may be required.
        unsigned m_padCount;             //!< Number of pad bytes.
        uint32_t m_length;               //!< Number of bytes to load.
        uint32_t m_address;              //!< Address to which data will be loaded.
        bool m_loadDCD;                  //!< Whether to execute the DCD after loading.
        uint32_t m_memoryId;             //!< Memory device ID.

        void fillPadding();
        uint32_t calculateCRC() const;
    };

    /*!
     * \brief Pattern fill bootloader command.
     */
    class FillCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        FillCommand();

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_FILL_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Address range
        //@{
        inline void setAddress(uint32_t address) { m_address = address; };
        inline uint32_t getAddress() const { return m_address; }
        inline void setFillCount(uint32_t count) { m_count = count; }
        inline uint32_t getFillCount() const { return m_count; }
        //@}

        //! \name Pattern
        //@{
        void setPattern(uint8_t pattern);
        void setPattern(uint16_t pattern);
        void setPattern(uint32_t pattern);

        inline uint32_t getPattern() const { return m_pattern; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        uint32_t m_address; //!< Fill start address.
        uint32_t m_count;   //!< Number of bytes to fill.
        uint32_t m_pattern; //!< Fill pattern.
    };

    /*!
     * \brief Change boot mode bootloader command.
     */
    class ModeCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        ModeCommand()
            : BootCommand()
            , m_mode(0)
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_MODE_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Boot mode
        //@{
        inline void setBootMode(uint32_t mode) { m_mode = mode; }
        inline uint32_t getBootMode() const { return m_mode; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        uint32_t m_mode; //!< New boot mode.
    };

    /*!
     * \brief Erase bootloader command.
     */
    class EraseCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        EraseCommand()
            : BootCommand()
            , m_doEraseAll(false)
            , m_doEraseAllUnsecure(false)
            , m_startAddress(0)
            , m_byteCount(0)
            , m_memoryId(0)
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_ERASE_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Erase all
        //@{
        void setEraseAll(bool doEraseAll) { m_doEraseAll = doEraseAll; }
        bool getEraseAll() const { return m_doEraseAll; }
        //@}

        //! \name Erase all unsecure
        //@{
        void setEraseAllUnsecure(bool doEraseAllUnsecure) { m_doEraseAllUnsecure = doEraseAllUnsecure; }
        bool getEraseAllUnsecure() const { return m_doEraseAllUnsecure; }
        //@}

        //! \name Erase address range
        //@{
        void setAddressRange(uint32_t startAddress, uint32_t count);
        void getAddressRange(uint32_t *startAddress, uint32_t *count) const;
        //@}

        //! \name Erase memory ID
        //@{
        void setMemoryId(uint32_t memId) { m_memoryId = memId; }
        uint32_t getMemoryId() const { return m_memoryId; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        bool m_doEraseAll;
        bool m_doEraseAllUnsecure;
        uint32_t m_startAddress;
        uint32_t m_byteCount;
        uint32_t m_memoryId;
    };

    /*!
    * \brief Program persistent bits bootloader command.
    */
    class ProgramCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        ProgramCommand()
            : BootCommand()
            , m_isEightByte(false)
            , m_index(0)
            , m_dataWord1(0)
            , m_dataWord2(0)
            , m_memoryId(0)
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_PROG_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Do 8-byte write.
        //@{
        void setIsEightByte(bool doIt) { m_isEightByte = doIt; }
        bool isEightByte() const { return m_isEightByte; }
        //@}

        //! \name Memory space id.
        //@{
        void setMemoryId(uint32_t id) { m_memoryId = id; }
        uint32_t getMemoryId() const { return m_memoryId; }
        //@}

        //! \name Persistent bit index
        //@{
        void setIndex(uint32_t index) { m_index = index; }
        uint32_t getIndex() const { return m_index; }
        //@}

        //! \name Data bytes 0-3
        //@{
        void setDataWord1(uint32_t dataWord) { m_dataWord1 = dataWord; }
        uint32_t getDataWord1() const { return m_dataWord1; }
        //@}

        //! \name Data bytes 4-7
        //@{
        void setDataWord2(uint32_t dataWord) { m_dataWord2 = dataWord; }
        uint32_t getDataWord2() const { return m_dataWord2; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        bool m_isEightByte;   //!< True if doing 8-byte write.
        uint32_t m_index;     //!< Index to write to.
        uint32_t m_dataWord1; //!< First 4 bytes to write.
        uint32_t m_dataWord2; //!< Second 4 bytes to write, valid if m_doEightByte is true.
        uint32_t m_memoryId;  //!< ID of memory space, e.g. 1=IFR0.
    };

    /*!
  * \brief Jump to address bootloader command.
  */
    class JumpCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        JumpCommand()
            : BootCommand()
            , m_address(0)
            , m_argument(0)
            , m_isHAB(false)
            , m_ivtSize(0)
            , m_stackPointer(0)
            , m_isStackPointerSet(false)
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_JUMP_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Accessors
        //@{
        inline void setAddress(uint32_t address) { m_address = address; }
        inline uint32_t getAddress() const { return m_address; }
        inline void setArgument(uint32_t argument) { m_argument = argument; }
        inline uint32_t getArgument() const { return m_argument; }
        inline void setIsHAB(bool isHAB) { m_isHAB = isHAB; }
        inline bool isHAB() const { return m_isHAB; }
        inline void setIVTSize(uint32_t ivtSize) { m_ivtSize = ivtSize; }
        inline uint32_t getIVTSize() const { return m_ivtSize; }
        inline void setStackPointer(uint32_t sp) { m_stackPointer = sp; }
        inline uint32_t getStackPointer() { return m_stackPointer; }
        inline void setIsStackPointerSet(bool isSP) { m_isStackPointerSet = isSP; }
        inline bool IsStackPointerSet() const { return m_isStackPointerSet; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        uint32_t m_address;      //!< Address of the code to execute.
        uint32_t m_argument;     //!< Sole argument to pass to code.
        uint32_t m_stackPointer; //!< Stack pointer for "jump sp" command.
        bool m_isHAB; //!< Whether this jump/call is a special HAB jump/call. When this flag is set, m_address becomes
                      //!the IVT address and m_ivtSize is the IVT size.
        bool m_isStackPointerSet; //! Whether this jump call has an initial stack pointer set.
        uint32_t m_ivtSize;       //!< Size of the IVT for a HAB jump/call.
    };

    /*!
     * \brief Call function bootloader command.
     */
    class CallCommand : public JumpCommand
    {
    public:
        //! \brief Default constructor.
        CallCommand()
            : JumpCommand()
        {
        }

        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_CALL_CMD; }
        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;
    };

    /*!
    * \brief Reset target bootloader command.
    */
    class ResetCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        ResetCommand()
            : BootCommand()
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_RESET_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
    };

    /*!
    * \brief Memory Enable bootloader command.
    */
    class MemEnableCommand : public BootCommand
    {
    public:
        //! \brief Default constructor.
        MemEnableCommand()
            : BootCommand()
            , m_startAddress(0)
            , m_byteCount(0)
            , m_memoryId(0)
        {
        }

        //! \brief Read the command contents from raw data.
        virtual void initFromData(const cipher_block_t *blocks, unsigned count, unsigned *consumed);

        //! \name Header
        //@{
        //! \brief Returns the tag value for this command.
        virtual uint8_t getTag() const { return ROM_MEM_ENABLE_CMD; }
        //! \brief Constructs the header for this boot command.
        virtual void fillCommandHeader(boot_command_t &header);
        //@}

        //! \name Memory enable address range
        //@{
        void setAddressRange(uint32_t startAddress, uint32_t count);
        void getAddressRange(uint32_t *startAddress, uint32_t *count) const;
        //@}

        //! \name Memory enable controller ID
        //@{
        void setMemoryId(uint32_t id) { m_memoryId = id; }
        uint32_t getMemoryId() const { return m_memoryId; }
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        uint32_t m_startAddress;
        uint32_t m_byteCount;
        uint32_t m_memoryId;
    };

    /*!
  * \brief Base class for a section of an Encore boot image.
  *
  * Provides methods to manage the unique identifier that all sections have, and
  * to set the boot image object which owns the section. There are also virtual
  * methods to get header flags and fill in the header used in the section
  * table. Subclasses must implement at least fillSectionHeader().
  */
    class Section : public CipherBlockGenerator
    {
    public:
        //! \brief Default constructor.
        Section()
            : CipherBlockGenerator()
            , m_identifier(0)
            , m_image(0)
            , m_alignment(BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT)
            , m_flags(0)
            , m_leaveUnencrypted(false)
        {
        }

        //! \brief Constructor taking the unique identifier for this section.
        Section(uint32_t identifier)
            : CipherBlockGenerator()
            , m_identifier(identifier)
            , m_image(0)
            , m_alignment(BOOT_IMAGE_MINIMUM_SECTION_ALIGNMENT)
            , m_flags(0)
            , m_leaveUnencrypted(false)
        {
        }

        //! \name Identifier
        //@{
        inline void setIdentifier(uint32_t identifier) { m_identifier = identifier; }
        inline uint32_t getIdentifier() const { return m_identifier; }
        //@}

        //! \name Header
        //@{
        //! \brief Sets explicit flags for this section.
        virtual void setFlags(uint32_t flags) { m_flags = flags; }
        //! \brief Returns the flags for this section.
        //!
        //! The return value consists of the flags set with setFlags() possibly or-ed
        //! with #ROM_SECTION_CLEARTEXT if the section has been set to be left
        //! unencrypted.
        virtual uint32_t getFlags() const { return m_flags | (m_leaveUnencrypted ? ROM_SECTION_CLEARTEXT : 0); }
        //! \brief Pure virtual method to construct the header for this section.
        virtual void fillSectionHeader(section_header_t &header);
        //@}

        //! \name Owner image
        //@{
        //! \brief Called when the section is added to an image.
        void setImage(EncoreBootImage *image) { m_image = image; }
        //! \brief Returns a pointer to the image that this section belongs to.
        EncoreBootImage *getImage() const { return m_image; }
        //@}

        //! \name Alignment
        //@{
        //! \brief Sets the required alignment in the output file for this section.
        void setAlignment(unsigned alignment);

        //! \brief Returns the current alignment, the minimum of which will be 16.
        unsigned getAlignment() const { return m_alignment; }
        //! \brief Computes padding amount for alignment requirement.
        unsigned getPadBlockCountForOffset(unsigned offset);
        //@}

        //! \name Leave unencrypted flag
        //@{
        //! \brief Sets whether the section will be left unencrypted.
        void setLeaveUnencrypted(unsigned flag) { m_leaveUnencrypted = flag; }
        //! \brief Returns true if the section will remain unencrypted.
        bool getLeaveUnencrypted() const { return m_leaveUnencrypted; }
        //@}

    protected:
        uint32_t m_identifier;    //!< Unique identifier for this section.
        EncoreBootImage *m_image; //!< The image to which this section belongs.
        unsigned m_alignment;     //!< Alignment requirement for the start of this section.
        uint32_t m_flags;         //!< Section flags set by the user.
        bool m_leaveUnencrypted;  //!< Set to true to prevent this section from being encrypted.
    };

    /*!
     * \brief A bootable section of an Encore boot image.
     */
    class BootSection : public Section
    {
    public:
        typedef std::list<BootCommand *> command_list_t;
        typedef command_list_t::iterator iterator_t;
        typedef command_list_t::const_iterator const_iterator_t;

    public:
        //! \brief Default constructor.
        BootSection()
            : Section()
        {
        }

        //! \brief Constructor taking the unique identifier for this section.
        BootSection(uint32_t identifier)
            : Section(identifier)
        {
        }

        //! \brief Destructor.
        virtual ~BootSection();

        //! \brief Load the section from raw data.
        virtual void fillFromData(const cipher_block_t *blocks, unsigned count);

        //! \name Header
        //@{
        //! \brief Returns the flags for this section.
        virtual uint32_t getFlags() const { return Section::getFlags() | ROM_SECTION_BOOTABLE; }
        //@}

        //! \name Commands
        //@{
        //! \brief Append a new command to the section.
        //!
        //! The section takes ownership of the command and will delete it when
        //! the section is destroyed.
        void addCommand(BootCommand *command) { m_commands.push_back(command); }
        //! \brief Returns the number of commands in this section, excluding the tag command.
        unsigned getCommandCount() const { return (unsigned)m_commands.size(); }
        iterator_t begin() { return m_commands.begin(); }
        iterator_t end() { return m_commands.end(); }
        const_iterator_t begin() const { return m_commands.begin(); }
        const_iterator_t end() const { return m_commands.end(); }
        //@}

        //! \name Cipher blocks
        //@{
        //! \brief Returns the total number of cipher blocks occupied by this section.
        virtual unsigned getBlockCount() const;

        //! \brief Returns the contents of up to \a maxCount cipher blocks.
        virtual unsigned getBlocks(unsigned offset, unsigned maxCount, cipher_block_t *data);
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        command_list_t m_commands; //!< Commands held in this section.

    protected:
        //! \brief Remove all commands from the section.
        void deleteCommands();
    };

    /*!
     * \brief A non-bootable section of an Encore boot image.
     */
    class DataSection : public Section
    {
    public:
        //! \brief Default constructor.
        DataSection()
            : Section()
            , m_data()
            , m_length(0)
        {
        }

        //! \brief Constructor taking the unique identifier for this section.
        DataSection(uint32_t identifier)
            : Section(identifier)
            , m_data()
            , m_length(0)
        {
        }

        //! \brief Set the data section's contents.
        void setData(const uint8_t *data, unsigned length);

        //! \brief Set the data section's contents without copying \a data.
        void setDataNoCopy(const uint8_t *data, unsigned length);

        //! \name Cipher blocks
        //@{
        //! \brief Returns the total number of cipher blocks occupied by this section.
        virtual unsigned getBlockCount() const;

        //! \brief Returns the contents of up to \a maxCount cipher blocks.
        virtual unsigned getBlocks(unsigned offset, unsigned maxCount, cipher_block_t *data);
        //@}

        //! \brief Print out a string representation of the object.
        virtual void debugPrint() const;

    protected:
        smart_array_ptr<uint8_t> m_data; //!< The section's contents.
        unsigned m_length;               //!< Number of bytes of data.
    };

public:
    typedef std::list<Section *> section_list_t;                     //!< List of image sections.
    typedef section_list_t::iterator section_iterator_t;             //!< Iterator over sections.
    typedef section_list_t::const_iterator const_section_iterator_t; //!< Const iterator over sections.

    typedef std::vector<AES128Key> key_list_t;               //!< List of KEKs.
    typedef key_list_t::iterator key_iterator_t;             //!< Iterator over KEKs.
    typedef key_list_t::const_iterator const_key_iterator_t; //!< Const iterator over KEKs.

public:
    //! \brief Default constructor.
    EncoreBootImage();

    //! \brief Destructor.
    virtual ~EncoreBootImage();

    //! \name Sections
    //@{
    void addSection(Section *newSection);
    inline unsigned sectionCount() const { return (unsigned)m_sections.size(); }
    inline section_iterator_t beginSection() { return m_sections.begin(); }
    inline section_iterator_t endSection() { return m_sections.end(); }
    inline const_section_iterator_t beginSection() const { return m_sections.begin(); }
    inline const_section_iterator_t endSection() const { return m_sections.end(); }
    section_iterator_t findSection(Section *section);

    //! \brief Calculates the starting block number for the given section.
    uint32_t getSectionOffset(Section *section);
    //@}

    //! \name Encryption keys
    //@{
    inline void addKey(const AES128Key &newKey) { m_keys.push_back(newKey); }
    inline unsigned keyCount() const { return (unsigned)m_keys.size(); }
    inline key_iterator_t beginKeys() { return m_keys.begin(); }
    inline key_iterator_t endKeys() { return m_keys.end(); }
    inline const_key_iterator_t beginKeys() const { return m_keys.begin(); }
    inline const_key_iterator_t endKeys() const { return m_keys.end(); }
    //! \brief The image is encrypted if there is at least one key.
    inline bool isEncrypted() const { return m_keys.size() != 0; }
    //@}

    //! \name Versions
    //@{
    virtual void setProductVersion(const version_t &version);
    virtual void setComponentVersion(const version_t &version);
    //@}

    //! \name Flags
    //@{
    inline void setFlags(uint16_t flags) { m_headerFlags = flags; }
    inline uint32_t getFlags() const { return m_headerFlags; }
    //@}

    //! \brief Specify the drive tag to be set in the output file header.
    virtual void setDriveTag(uint16_t tag) { m_driveTag = tag; }

    //! \brief Specify the build number, however build number for EncoreBuildImage is not available.
    virtual void setBuildNumber(uint16_t) {;}

    //! \brief Calculates the total number of cipher blocks the image consumes.
    uint32_t getImageSize();

    //! \brief Returns the preferred ".sb" extension for Encore boot images.
    virtual std::string getFileExtension() const { return ".sb"; }
    //! \name Output
    //@{
    //! \brief Write the boot image to an output stream.
    virtual void writeToStream(std::ostream &stream);
    //@}

    //! \brief Print out a string representation of the object.
    virtual void debugPrint() const;

protected:
    uint16_t m_headerFlags;       //!< Flags field in the boot image header.
    version_t m_productVersion;   //!< Product version.
    version_t m_componentVersion; //!< Component version.
    uint16_t m_driveTag;          //!< System drive tag for this boot image.
    section_list_t m_sections;    //!< Sections contained in this image.
    key_list_t m_keys;            //!< List of key encryption keys. If empty, the image is unencrypted.
    AES128Key m_sessionKey;       //!< Session key we're using.

    void prepareImageHeader(boot_image_header_t &header);
    uint64_t getTimestamp();
    Section *findFirstBootableSection();
    unsigned getPadBlockCountForSection(Section *section, unsigned offset);
};

}; // namespace elftosb

#endif // _EncoreBootImage_h_
