diff mbox series

tools: zynqimage: Add partition read support

Message ID 20180315214922.58745-1-agraf@suse.de
State New
Headers show
Series tools: zynqimage: Add partition read support | expand

Commit Message

Alexander Graf March 15, 2018, 9:49 p.m. UTC
The zynqmp image format has support for inline partitions which are
used by FSBL to describe payloads that are loaded by FSBL itself.

While we can't create images that contain partitions (yet), we should
still at least be able to examine them and show the user what's inside
when we analyze an image created by bootgen.

Signed-off-by: Alexander Graf <agraf@suse.de>
---
 tools/zynqmpimage.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 155 insertions(+), 1 deletion(-)

Comments

Nathan Rossi March 16, 2018, 6:48 a.m. UTC | #1
On 16 March 2018 at 07:49, Alexander Graf <agraf@suse.de> wrote:
> The zynqmp image format has support for inline partitions which are
> used by FSBL to describe payloads that are loaded by FSBL itself.
>
> While we can't create images that contain partitions (yet), we should
> still at least be able to examine them and show the user what's inside
> when we analyze an image created by bootgen.
>
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  tools/zynqmpimage.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 155 insertions(+), 1 deletion(-)
>
> diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c
> index f48ac6dbe5..5183ea9bc8 100644
> --- a/tools/zynqmpimage.c
> +++ b/tools/zynqmpimage.c
> @@ -80,6 +80,71 @@ struct zynqmp_reginit {
>  #define HEADER_INTERRUPT_VECTORS       8
>  #define HEADER_REGINITS                        256
>

Please make sure to update the comment at the start of the file or add
one here to state where these data structures are documented. For
ZynqMP you likely got it from UG1137? This makes it easy for others to
refer back to the documentation, as well as for legal reasons so that
it is clear the details come from the user guide and not the FSBL
source. Since for Zynq the FSBL specific image/partition table headers
were only documented by Xilinx in the FSBL source itself, which is
licensed such that it is incompatible with GPL.

Thanks,
Nathan

> +struct image_header_table {
> +       uint32_t version;
> +       uint32_t nr_parts;
> +       uint32_t partition_header_offset; /* divided by 4 */
> +       uint32_t __reserved1;
> +       uint32_t auth_certificate_offset;
> +       uint32_t partition_present_device;
> +       uint32_t __reserved2[9];
> +       uint32_t checksum;
> +};
> +
> +#define PART_ATTR_VEC_LOCATION         0x800000
> +#define PART_ATTR_BLOCK_SIZE_MASK      0x700000
> +#define PART_ATTR_BIG_ENDIAN           0x040000
> +#define PART_ATTR_PART_OWNER_MASK      0x030000
> +#define     PART_ATTR_PART_OWNER_FSBL          0x000000
> +#define PART_ATTR_RSA_SIG              0x008000
> +#define PART_ATTR_CHECKSUM_MASK                0x007000
> +#define    PART_ATTR_CHECKSUM_NONE             0x000000
> +#define    PART_ATTR_CHECKSUM_MD5              0x001000
> +#define    PART_ATTR_CHECKSUM_SHA2             0x002000
> +#define    PART_ATTR_CHECKSUM_SHA3             0x003000
> +#define PART_ATTR_DEST_CPU_MASK                0x000f00
> +#define    PART_ATTR_DEST_CPU_NONE             0x000000
> +#define    PART_ATTR_DEST_CPU_A53_0            0x000100
> +#define    PART_ATTR_DEST_CPU_A53_1            0x000200
> +#define    PART_ATTR_DEST_CPU_A53_2            0x000300
> +#define    PART_ATTR_DEST_CPU_A53_3            0x000400
> +#define    PART_ATTR_DEST_CPU_R5_0             0x000500
> +#define    PART_ATTR_DEST_CPU_R5_1             0x000600
> +#define    PART_ATTR_DEST_CPU_R5_L             0x000700
> +#define    PART_ATTR_DEST_CPU_PMU              0x000800
> +#define PART_ATTR_ENCRYPTED            0x000080
> +#define PART_ATTR_DEST_DEVICE_MASK     0x000070
> +#define    PART_ATTR_DEST_DEVICE_NONE          0x000000
> +#define    PART_ATTR_DEST_DEVICE_PS            0x000010
> +#define    PART_ATTR_DEST_DEVICE_PL            0x000020
> +#define    PART_ATTR_DEST_DEVICE_PMU           0x000030
> +#define PART_ATTR_A53_EXEC_AARCH32     0x000008
> +#define PART_ATTR_TARGET_EL_MASK       0x000006
> +#define PART_ATTR_TR_SECURE_MASK       0x000001
> +
> +static const char *dest_cpus[0x10] = {
> +       "none", "A53-0", "A53-1", "A53-2", "A53-3", "R5-0", "R5-1",
> +       "R5-L", "PMU", "unknown", "unknown", "unknown", "unknown",
> +       "unknown", "unknown", "unknown"
> +};
> +
> +struct partition_header {
> +       uint32_t len_enc; /* divided by 4 */
> +       uint32_t len_unenc; /* divided by 4 */
> +       uint32_t len; /* divided by 4 */
> +       uint32_t next_partition_offset;
> +       uint64_t entry_point;
> +       uint64_t load_address;
> +       uint32_t offset; /* divided by 4 */
> +       uint32_t attributes;
> +       uint32_t section_count;
> +       uint32_t checksum_offset; /* divided by 4 */
> +       uint32_t image_header_offset;
> +       uint32_t auth_certificate_offset;
> +       uint32_t __reserved1[1];
> +       uint32_t checksum;
> +};
> +
>  struct zynqmp_header {
>         uint32_t interrupt_vectors[HEADER_INTERRUPT_VECTORS]; /* 0x0 */
>         uint32_t width_detection; /* 0x20 */
> @@ -93,7 +158,9 @@ struct zynqmp_header {
>         uint32_t image_stored_size; /* 0x40 */
>         uint32_t image_attributes; /* 0x44 */
>         uint32_t checksum; /* 0x48 */
> -       uint32_t __reserved1[27]; /* 0x4c */
> +       uint32_t __reserved1[19]; /* 0x4c */
> +       uint32_t image_header_table_offset; /* 0x98 */
> +       uint32_t __reserved2[7]; /* 0x9c */
>         struct zynqmp_reginit register_init[HEADER_REGINITS]; /* 0xb8 */
>         uint32_t __reserved4[66]; /* 0x9c0 */
>  };
> @@ -173,6 +240,74 @@ static int zynqmpimage_verify_header(unsigned char *ptr, int image_size,
>         return 0;
>  }
>
> +static void print_partition(const void *ptr, const struct partition_header *ph)
> +{
> +       uint32_t attr = le32_to_cpu(ph->attributes);
> +       const char *part_owner;
> +       const char *dest_devs[0x8] = {
> +               "none", "PS", "PL", "PMU", "unknown", "unknown", "unknown",
> +               "unknown"
> +       };
> +
> +       if ((attr & PART_ATTR_PART_OWNER_MASK) == PART_ATTR_PART_OWNER_FSBL)
> +               part_owner = "FSBL";
> +       else
> +               part_owner = "Unknown";
> +
> +       printf("%s payload on CPU %s (%s):\n",
> +               part_owner,
> +               dest_cpus[(attr & PART_ATTR_DEST_CPU_MASK) >> 8],
> +               dest_devs[(attr & PART_ATTR_DEST_DEVICE_MASK) >> 4]);
> +
> +       printf("    Offset     : 0x%08x\n", le32_to_cpu(ph->offset));
> +       printf("    Size       : %lu bytes\n",
> +               (unsigned long)le32_to_cpu(ph->len) * 4);
> +       printf("    Load       : 0x%08llx",
> +               (unsigned long long)le64_to_cpu(ph->load_address));
> +       if (ph->load_address != ph->entry_point)
> +               printf(" (entry=0x%08llx)\n",
> +               (unsigned long long)le64_to_cpu(ph->entry_point));
> +       else
> +               printf("\n");
> +       printf("    Attributes : ");
> +
> +       if (attr & PART_ATTR_VEC_LOCATION)
> +               printf("vec ");
> +
> +       if (attr & PART_ATTR_ENCRYPTED)
> +               printf("encrypted ");
> +
> +       switch (attr & PART_ATTR_CHECKSUM_MASK) {
> +       case PART_ATTR_CHECKSUM_MD5:
> +               printf("md5 ");
> +               break;
> +       case PART_ATTR_CHECKSUM_SHA2:
> +               printf("sha2 ");
> +               break;
> +       case PART_ATTR_CHECKSUM_SHA3:
> +               printf("sha3 ");
> +               break;
> +       }
> +
> +       if (attr & PART_ATTR_BIG_ENDIAN)
> +               printf("BigEndian ");
> +
> +       if (attr & PART_ATTR_RSA_SIG)
> +               printf("RSA ");
> +
> +       if (attr & PART_ATTR_A53_EXEC_AARCH32)
> +               printf("AArch32 ");
> +
> +       if (attr & PART_ATTR_TARGET_EL_MASK)
> +               printf("EL%d ", (attr & PART_ATTR_TARGET_EL_MASK) >> 1);
> +
> +       if (attr & PART_ATTR_TR_SECURE_MASK)
> +               printf("secure ");
> +       printf("\n");
> +
> +       printf("    Checksum   : 0x%08x\n", le32_to_cpu(ph->checksum));
> +}
> +
>  static void zynqmpimage_print_header(const void *ptr)
>  {
>         struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
> @@ -213,6 +348,25 @@ static void zynqmpimage_print_header(const void *ptr)
>                        le32_to_cpu(zynqhdr->register_init[i].data));
>         }
>
> +       if (zynqhdr->image_header_table_offset) {
> +               struct image_header_table *iht = (void*)ptr +
> +                       zynqhdr->image_header_table_offset;
> +               struct partition_header *ph;
> +               uint32_t ph_offset;
> +               int i;
> +
> +               ph_offset = le32_to_cpu(iht->partition_header_offset) * 4;
> +               ph = (void*)ptr + ph_offset;
> +               for (i = 0; i < le32_to_cpu(iht->nr_parts); i++) {
> +                       uint32_t next = le32_to_cpu(ph->next_partition_offset) * 4;
> +
> +                       /* Partition 0 is the base image itself */
> +                       if (i)
> +                               print_partition(ptr, ph);
> +                       ph = (void*)ptr + next;
> +               }
> +       }
> +
>         free(dynamic_header);
>  }
>
> --
> 2.12.3
>
> _______________________________________________
> U-Boot mailing list
> U-Boot@lists.denx.de
> https://lists.denx.de/listinfo/u-boot
Michal Simek March 16, 2018, 11:41 a.m. UTC | #2
On 15.3.2018 22:49, Alexander Graf wrote:
> The zynqmp image format has support for inline partitions which are
> used by FSBL to describe payloads that are loaded by FSBL itself.
> 
> While we can't create images that contain partitions (yet), we should
> still at least be able to examine them and show the user what's inside
> when we analyze an image created by bootgen.
> 
> Signed-off-by: Alexander Graf <agraf@suse.de>
> ---
>  tools/zynqmpimage.c | 156 +++++++++++++++++++++++++++++++++++++++++++++++++++-
>  1 file changed, 155 insertions(+), 1 deletion(-)
> 
> diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c
> index f48ac6dbe5..5183ea9bc8 100644
> --- a/tools/zynqmpimage.c
> +++ b/tools/zynqmpimage.c
> @@ -80,6 +80,71 @@ struct zynqmp_reginit {
>  #define HEADER_INTERRUPT_VECTORS	8
>  #define HEADER_REGINITS			256
>  
> +struct image_header_table {
> +	uint32_t version;
> +	uint32_t nr_parts;
> +	uint32_t partition_header_offset; /* divided by 4 */
> +	uint32_t __reserved1;
> +	uint32_t auth_certificate_offset;
> +	uint32_t partition_present_device;
> +	uint32_t __reserved2[9];
> +	uint32_t checksum;
> +};
> +
> +#define PART_ATTR_VEC_LOCATION		0x800000
> +#define PART_ATTR_BLOCK_SIZE_MASK	0x700000
> +#define PART_ATTR_BIG_ENDIAN		0x040000
> +#define PART_ATTR_PART_OWNER_MASK	0x030000
> +#define     PART_ATTR_PART_OWNER_FSBL		0x000000
> +#define PART_ATTR_RSA_SIG		0x008000
> +#define PART_ATTR_CHECKSUM_MASK		0x007000
> +#define    PART_ATTR_CHECKSUM_NONE		0x000000
> +#define    PART_ATTR_CHECKSUM_MD5		0x001000
> +#define    PART_ATTR_CHECKSUM_SHA2		0x002000
> +#define    PART_ATTR_CHECKSUM_SHA3		0x003000
> +#define PART_ATTR_DEST_CPU_MASK		0x000f00
> +#define    PART_ATTR_DEST_CPU_NONE		0x000000
> +#define    PART_ATTR_DEST_CPU_A53_0		0x000100
> +#define    PART_ATTR_DEST_CPU_A53_1		0x000200
> +#define    PART_ATTR_DEST_CPU_A53_2		0x000300
> +#define    PART_ATTR_DEST_CPU_A53_3		0x000400
> +#define    PART_ATTR_DEST_CPU_R5_0		0x000500
> +#define    PART_ATTR_DEST_CPU_R5_1		0x000600
> +#define    PART_ATTR_DEST_CPU_R5_L		0x000700
> +#define    PART_ATTR_DEST_CPU_PMU		0x000800
> +#define PART_ATTR_ENCRYPTED		0x000080
> +#define PART_ATTR_DEST_DEVICE_MASK	0x000070
> +#define    PART_ATTR_DEST_DEVICE_NONE		0x000000
> +#define    PART_ATTR_DEST_DEVICE_PS		0x000010
> +#define    PART_ATTR_DEST_DEVICE_PL		0x000020
> +#define    PART_ATTR_DEST_DEVICE_PMU		0x000030
> +#define PART_ATTR_A53_EXEC_AARCH32	0x000008
> +#define PART_ATTR_TARGET_EL_MASK	0x000006
> +#define PART_ATTR_TR_SECURE_MASK	0x000001
> +
> +static const char *dest_cpus[0x10] = {
> +	"none", "A53-0", "A53-1", "A53-2", "A53-3", "R5-0", "R5-1",
> +	"R5-L", "PMU", "unknown", "unknown", "unknown", "unknown",
> +	"unknown", "unknown", "unknown"
> +};
> +
> +struct partition_header {
> +	uint32_t len_enc; /* divided by 4 */
> +	uint32_t len_unenc; /* divided by 4 */
> +	uint32_t len; /* divided by 4 */
> +	uint32_t next_partition_offset;
> +	uint64_t entry_point;
> +	uint64_t load_address;
> +	uint32_t offset; /* divided by 4 */
> +	uint32_t attributes;
> +	uint32_t section_count;
> +	uint32_t checksum_offset; /* divided by 4 */
> +	uint32_t image_header_offset;
> +	uint32_t auth_certificate_offset;
> +	uint32_t __reserved1[1];
> +	uint32_t checksum;
> +};
> +
>  struct zynqmp_header {
>  	uint32_t interrupt_vectors[HEADER_INTERRUPT_VECTORS]; /* 0x0 */
>  	uint32_t width_detection; /* 0x20 */
> @@ -93,7 +158,9 @@ struct zynqmp_header {
>  	uint32_t image_stored_size; /* 0x40 */
>  	uint32_t image_attributes; /* 0x44 */
>  	uint32_t checksum; /* 0x48 */
> -	uint32_t __reserved1[27]; /* 0x4c */
> +	uint32_t __reserved1[19]; /* 0x4c */
> +	uint32_t image_header_table_offset; /* 0x98 */
> +	uint32_t __reserved2[7]; /* 0x9c */
>  	struct zynqmp_reginit register_init[HEADER_REGINITS]; /* 0xb8 */
>  	uint32_t __reserved4[66]; /* 0x9c0 */
>  };
> @@ -173,6 +240,74 @@ static int zynqmpimage_verify_header(unsigned char *ptr, int image_size,
>  	return 0;
>  }
>  
> +static void print_partition(const void *ptr, const struct partition_header *ph)
> +{
> +	uint32_t attr = le32_to_cpu(ph->attributes);
> +	const char *part_owner;
> +	const char *dest_devs[0x8] = {
> +		"none", "PS", "PL", "PMU", "unknown", "unknown", "unknown",
> +		"unknown"
> +	};
> +
> +	if ((attr & PART_ATTR_PART_OWNER_MASK) == PART_ATTR_PART_OWNER_FSBL)
> +		part_owner = "FSBL";
> +	else
> +		part_owner = "Unknown";
> +
> +	printf("%s payload on CPU %s (%s):\n",
> +		part_owner,
> +		dest_cpus[(attr & PART_ATTR_DEST_CPU_MASK) >> 8],
> +		dest_devs[(attr & PART_ATTR_DEST_DEVICE_MASK) >> 4]);
> +
> +	printf("    Offset     : 0x%08x\n", le32_to_cpu(ph->offset));

This offset is multiply by 4 too.

> +	printf("    Size       : %lu bytes\n",

This should be probably in hex to be aligned with the rest of
information there. Or the best to show in both if you want to extract
parts with dd for example.

> +		(unsigned long)le32_to_cpu(ph->len) * 4);
> +	printf("    Load       : 0x%08llx",
> +		(unsigned long long)le64_to_cpu(ph->load_address));
> +	if (ph->load_address != ph->entry_point)
> +		printf(" (entry=0x%08llx)\n",
> +		(unsigned long long)le64_to_cpu(ph->entry_point));
> +	else
> +		printf("\n");
> +	printf("    Attributes : ");
> +
> +	if (attr & PART_ATTR_VEC_LOCATION)
> +		printf("vec ");
> +
> +	if (attr & PART_ATTR_ENCRYPTED)
> +		printf("encrypted ");
> +
> +	switch (attr & PART_ATTR_CHECKSUM_MASK) {
> +	case PART_ATTR_CHECKSUM_MD5:
> +		printf("md5 ");
> +		break;
> +	case PART_ATTR_CHECKSUM_SHA2:
> +		printf("sha2 ");
> +		break;
> +	case PART_ATTR_CHECKSUM_SHA3:
> +		printf("sha3 ");
> +		break;
> +	}
> +
> +	if (attr & PART_ATTR_BIG_ENDIAN)
> +		printf("BigEndian ");
> +
> +	if (attr & PART_ATTR_RSA_SIG)
> +		printf("RSA ");
> +
> +	if (attr & PART_ATTR_A53_EXEC_AARCH32)
> +		printf("AArch32 ");
> +
> +	if (attr & PART_ATTR_TARGET_EL_MASK)
> +		printf("EL%d ", (attr & PART_ATTR_TARGET_EL_MASK) >> 1);
> +
> +	if (attr & PART_ATTR_TR_SECURE_MASK)
> +		printf("secure ");
> +	printf("\n");
> +
> +	printf("    Checksum   : 0x%08x\n", le32_to_cpu(ph->checksum));
> +}
> +
>  static void zynqmpimage_print_header(const void *ptr)
>  {
>  	struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
> @@ -213,6 +348,25 @@ static void zynqmpimage_print_header(const void *ptr)
>  		       le32_to_cpu(zynqhdr->register_init[i].data));
>  	}
>  
> +	if (zynqhdr->image_header_table_offset) {
> +		struct image_header_table *iht = (void*)ptr +
> +			zynqhdr->image_header_table_offset;
> +		struct partition_header *ph;
> +		uint32_t ph_offset;
> +		int i;
> +
> +		ph_offset = le32_to_cpu(iht->partition_header_offset) * 4;
> +		ph = (void*)ptr + ph_offset;
> +		for (i = 0; i < le32_to_cpu(iht->nr_parts); i++) {
> +			uint32_t next = le32_to_cpu(ph->next_partition_offset) * 4;
> +
> +			/* Partition 0 is the base image itself */
> +			if (i)
> +				print_partition(ptr, ph);
> +			ph = (void*)ptr + next;
> +		}
> +	}
> +
>  	free(dynamic_header);
>  }
>  
> 

Thanks,
Michal
diff mbox series

Patch

diff --git a/tools/zynqmpimage.c b/tools/zynqmpimage.c
index f48ac6dbe5..5183ea9bc8 100644
--- a/tools/zynqmpimage.c
+++ b/tools/zynqmpimage.c
@@ -80,6 +80,71 @@  struct zynqmp_reginit {
 #define HEADER_INTERRUPT_VECTORS	8
 #define HEADER_REGINITS			256
 
+struct image_header_table {
+	uint32_t version;
+	uint32_t nr_parts;
+	uint32_t partition_header_offset; /* divided by 4 */
+	uint32_t __reserved1;
+	uint32_t auth_certificate_offset;
+	uint32_t partition_present_device;
+	uint32_t __reserved2[9];
+	uint32_t checksum;
+};
+
+#define PART_ATTR_VEC_LOCATION		0x800000
+#define PART_ATTR_BLOCK_SIZE_MASK	0x700000
+#define PART_ATTR_BIG_ENDIAN		0x040000
+#define PART_ATTR_PART_OWNER_MASK	0x030000
+#define     PART_ATTR_PART_OWNER_FSBL		0x000000
+#define PART_ATTR_RSA_SIG		0x008000
+#define PART_ATTR_CHECKSUM_MASK		0x007000
+#define    PART_ATTR_CHECKSUM_NONE		0x000000
+#define    PART_ATTR_CHECKSUM_MD5		0x001000
+#define    PART_ATTR_CHECKSUM_SHA2		0x002000
+#define    PART_ATTR_CHECKSUM_SHA3		0x003000
+#define PART_ATTR_DEST_CPU_MASK		0x000f00
+#define    PART_ATTR_DEST_CPU_NONE		0x000000
+#define    PART_ATTR_DEST_CPU_A53_0		0x000100
+#define    PART_ATTR_DEST_CPU_A53_1		0x000200
+#define    PART_ATTR_DEST_CPU_A53_2		0x000300
+#define    PART_ATTR_DEST_CPU_A53_3		0x000400
+#define    PART_ATTR_DEST_CPU_R5_0		0x000500
+#define    PART_ATTR_DEST_CPU_R5_1		0x000600
+#define    PART_ATTR_DEST_CPU_R5_L		0x000700
+#define    PART_ATTR_DEST_CPU_PMU		0x000800
+#define PART_ATTR_ENCRYPTED		0x000080
+#define PART_ATTR_DEST_DEVICE_MASK	0x000070
+#define    PART_ATTR_DEST_DEVICE_NONE		0x000000
+#define    PART_ATTR_DEST_DEVICE_PS		0x000010
+#define    PART_ATTR_DEST_DEVICE_PL		0x000020
+#define    PART_ATTR_DEST_DEVICE_PMU		0x000030
+#define PART_ATTR_A53_EXEC_AARCH32	0x000008
+#define PART_ATTR_TARGET_EL_MASK	0x000006
+#define PART_ATTR_TR_SECURE_MASK	0x000001
+
+static const char *dest_cpus[0x10] = {
+	"none", "A53-0", "A53-1", "A53-2", "A53-3", "R5-0", "R5-1",
+	"R5-L", "PMU", "unknown", "unknown", "unknown", "unknown",
+	"unknown", "unknown", "unknown"
+};
+
+struct partition_header {
+	uint32_t len_enc; /* divided by 4 */
+	uint32_t len_unenc; /* divided by 4 */
+	uint32_t len; /* divided by 4 */
+	uint32_t next_partition_offset;
+	uint64_t entry_point;
+	uint64_t load_address;
+	uint32_t offset; /* divided by 4 */
+	uint32_t attributes;
+	uint32_t section_count;
+	uint32_t checksum_offset; /* divided by 4 */
+	uint32_t image_header_offset;
+	uint32_t auth_certificate_offset;
+	uint32_t __reserved1[1];
+	uint32_t checksum;
+};
+
 struct zynqmp_header {
 	uint32_t interrupt_vectors[HEADER_INTERRUPT_VECTORS]; /* 0x0 */
 	uint32_t width_detection; /* 0x20 */
@@ -93,7 +158,9 @@  struct zynqmp_header {
 	uint32_t image_stored_size; /* 0x40 */
 	uint32_t image_attributes; /* 0x44 */
 	uint32_t checksum; /* 0x48 */
-	uint32_t __reserved1[27]; /* 0x4c */
+	uint32_t __reserved1[19]; /* 0x4c */
+	uint32_t image_header_table_offset; /* 0x98 */
+	uint32_t __reserved2[7]; /* 0x9c */
 	struct zynqmp_reginit register_init[HEADER_REGINITS]; /* 0xb8 */
 	uint32_t __reserved4[66]; /* 0x9c0 */
 };
@@ -173,6 +240,74 @@  static int zynqmpimage_verify_header(unsigned char *ptr, int image_size,
 	return 0;
 }
 
+static void print_partition(const void *ptr, const struct partition_header *ph)
+{
+	uint32_t attr = le32_to_cpu(ph->attributes);
+	const char *part_owner;
+	const char *dest_devs[0x8] = {
+		"none", "PS", "PL", "PMU", "unknown", "unknown", "unknown",
+		"unknown"
+	};
+
+	if ((attr & PART_ATTR_PART_OWNER_MASK) == PART_ATTR_PART_OWNER_FSBL)
+		part_owner = "FSBL";
+	else
+		part_owner = "Unknown";
+
+	printf("%s payload on CPU %s (%s):\n",
+		part_owner,
+		dest_cpus[(attr & PART_ATTR_DEST_CPU_MASK) >> 8],
+		dest_devs[(attr & PART_ATTR_DEST_DEVICE_MASK) >> 4]);
+
+	printf("    Offset     : 0x%08x\n", le32_to_cpu(ph->offset));
+	printf("    Size       : %lu bytes\n",
+		(unsigned long)le32_to_cpu(ph->len) * 4);
+	printf("    Load       : 0x%08llx",
+		(unsigned long long)le64_to_cpu(ph->load_address));
+	if (ph->load_address != ph->entry_point)
+		printf(" (entry=0x%08llx)\n",
+		(unsigned long long)le64_to_cpu(ph->entry_point));
+	else
+		printf("\n");
+	printf("    Attributes : ");
+
+	if (attr & PART_ATTR_VEC_LOCATION)
+		printf("vec ");
+
+	if (attr & PART_ATTR_ENCRYPTED)
+		printf("encrypted ");
+
+	switch (attr & PART_ATTR_CHECKSUM_MASK) {
+	case PART_ATTR_CHECKSUM_MD5:
+		printf("md5 ");
+		break;
+	case PART_ATTR_CHECKSUM_SHA2:
+		printf("sha2 ");
+		break;
+	case PART_ATTR_CHECKSUM_SHA3:
+		printf("sha3 ");
+		break;
+	}
+
+	if (attr & PART_ATTR_BIG_ENDIAN)
+		printf("BigEndian ");
+
+	if (attr & PART_ATTR_RSA_SIG)
+		printf("RSA ");
+
+	if (attr & PART_ATTR_A53_EXEC_AARCH32)
+		printf("AArch32 ");
+
+	if (attr & PART_ATTR_TARGET_EL_MASK)
+		printf("EL%d ", (attr & PART_ATTR_TARGET_EL_MASK) >> 1);
+
+	if (attr & PART_ATTR_TR_SECURE_MASK)
+		printf("secure ");
+	printf("\n");
+
+	printf("    Checksum   : 0x%08x\n", le32_to_cpu(ph->checksum));
+}
+
 static void zynqmpimage_print_header(const void *ptr)
 {
 	struct zynqmp_header *zynqhdr = (struct zynqmp_header *)ptr;
@@ -213,6 +348,25 @@  static void zynqmpimage_print_header(const void *ptr)
 		       le32_to_cpu(zynqhdr->register_init[i].data));
 	}
 
+	if (zynqhdr->image_header_table_offset) {
+		struct image_header_table *iht = (void*)ptr +
+			zynqhdr->image_header_table_offset;
+		struct partition_header *ph;
+		uint32_t ph_offset;
+		int i;
+
+		ph_offset = le32_to_cpu(iht->partition_header_offset) * 4;
+		ph = (void*)ptr + ph_offset;
+		for (i = 0; i < le32_to_cpu(iht->nr_parts); i++) {
+			uint32_t next = le32_to_cpu(ph->next_partition_offset) * 4;
+
+			/* Partition 0 is the base image itself */
+			if (i)
+				print_partition(ptr, ph);
+			ph = (void*)ptr + next;
+		}
+	}
+
 	free(dynamic_header);
 }