diff mbox

[U-Boot,v3] fastboot: handle flash write to GPT partitions

Message ID 1418262002-14194-1-git-send-email-srae@broadcom.com
State Changes Requested
Delegated to: Łukasz Majewski
Headers show

Commit Message

Steve Rae Dec. 11, 2014, 1:40 a.m. UTC
Implement a feature to allow fastboot to write the downloaded image
to the space reserved for the Protective MBR and the Primary GUID
Partition Table.
Additionally, prepare and write the Backup GUID Partition Table.

Signed-off-by: Steve Rae <srae@broadcom.com>
---

Changes in v3:
- prefer leXX_to_cpu() over cpu_to_leXX()
- enhance calculation of pointer to GPT Entries
- prepare and write the Backup GPT
   (requested by: Lukasz Majewski <l.majewski@samsung.com>)

Changes in v2:
add validation of the GPT before writing to flash
(suggested by: Lukasz Majewski <l.majewski@samsung.com>)

 README          |   9 +++++
 common/fb_mmc.c |  26 ++++++++++++--
 disk/part_efi.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 include/part.h  |  20 +++++++++++
 4 files changed, 159 insertions(+), 3 deletions(-)

Comments

Łukasz Majewski Dec. 11, 2014, 10:12 a.m. UTC | #1
Hi Steve,

> Implement a feature to allow fastboot to write the downloaded image
> to the space reserved for the Protective MBR and the Primary GUID
> Partition Table.
> Additionally, prepare and write the Backup GUID Partition Table.
> 
> Signed-off-by: Steve Rae <srae@broadcom.com>
> ---
> 
> Changes in v3:
> - prefer leXX_to_cpu() over cpu_to_leXX()
> - enhance calculation of pointer to GPT Entries
> - prepare and write the Backup GPT
>    (requested by: Lukasz Majewski <l.majewski@samsung.com>)
> 
> Changes in v2:
> add validation of the GPT before writing to flash
> (suggested by: Lukasz Majewski <l.majewski@samsung.com>)
> 
>  README          |   9 +++++
>  common/fb_mmc.c |  26 ++++++++++++--
>  disk/part_efi.c | 107
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++
> include/part.h  |  20 +++++++++++ 4 files changed, 159 insertions(+),
> 3 deletions(-)
> 
> diff --git a/README b/README
> index 4ca04d0..42ece99 100644
> --- a/README
> +++ b/README
> @@ -1773,6 +1773,15 @@ The following options need to be configured:
>  		regarding the non-volatile storage device. Define
> this to the eMMC device that fastboot should use to store the image.
>  
> +		CONFIG_FASTBOOT_GPT_NAME
> +		The fastboot "flash" command supports writing the
> downloaded
> +		image to the Protective MBR and the Primary GUID
> Partition
> +		Table. (Additionally, this downloaded image is
> post-processed
> +		to generate and write the Backup GUID Partition
> Table.)
> +		This occurs when the specified "partition name" on
> the
> +		"fastboot flash" command line matches this value.
> +		Default is GPT_ENTRY_NAME (currently "gpt") if
> undefined. +
>  - Journaling Flash filesystem support:
>  		CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF,
> CONFIG_JFFS2_NAND_SIZE, CONFIG_JFFS2_NAND_DEV
> diff --git a/common/fb_mmc.c b/common/fb_mmc.c
> index fb06d8a..5acc8df 100644
> --- a/common/fb_mmc.c
> +++ b/common/fb_mmc.c
> @@ -4,12 +4,17 @@
>   * SPDX-License-Identifier:	GPL-2.0+
>   */
>  
> +#include <config.h>
>  #include <common.h>
>  #include <fb_mmc.h>
>  #include <part.h>
>  #include <aboot.h>
>  #include <sparse_format.h>
>  
> +#ifndef CONFIG_FASTBOOT_GPT_NAME
> +#define CONFIG_FASTBOOT_GPT_NAME GPT_ENTRY_NAME
> +#endif
> +
>  /* The 64 defined bytes plus the '\0' */
>  #define RESPONSE_LEN	(64 + 1)
>  
> @@ -62,7 +67,6 @@ static void write_raw_image(block_dev_desc_t
> *dev_desc, disk_partition_t *info, void fb_mmc_flash_write(const char
> *cmd, void *download_buffer, unsigned int download_bytes, char
> *response) {
> -	int ret;
>  	block_dev_desc_t *dev_desc;
>  	disk_partition_t info;
>  
> @@ -76,8 +80,24 @@ void fb_mmc_flash_write(const char *cmd, void
> *download_buffer, return;
>  	}
>  
> -	ret = get_partition_info_efi_by_name(dev_desc, cmd, &info);
> -	if (ret) {
> +	if (strcmp(cmd, CONFIG_FASTBOOT_GPT_NAME) == 0) {
> +		printf("%s: updating MBR, Primary and Backup
> GPT(s)\n",
> +		       __func__);
> +		if (is_valid_gpt_buf(download_buffer,
> dev_desc->blksz)) {
> +			printf("%s: invalid GPT - refusing to write
> to flash\n",
> +			       __func__);
> +			fastboot_fail("invalid GPT partition");
> +			return;
> +		}
> +		if (write_mbr_and_gpt_partitions(dev_desc,
> download_buffer)) {
> +			printf("%s: writing GPT partitions
> failed\n", __func__);
> +			fastboot_fail("writing GPT partitions
> failed");
> +			return;
> +		}
> +		printf("........ success\n");
> +		fastboot_okay("");
> +		return;
> +	} else if (get_partition_info_efi_by_name(dev_desc, cmd,
> &info)) { error("cannot find partition: '%s'\n", cmd);
>  		fastboot_fail("cannot find partition");
>  		return;
> diff --git a/disk/part_efi.c b/disk/part_efi.c
> index efed58f..9cc04da 100644
> --- a/disk/part_efi.c
> +++ b/disk/part_efi.c
> @@ -455,6 +455,113 @@ err:
>  	free(gpt_h);
>  	return ret;
>  }
> +
> +int is_valid_gpt_buf(void *buf, unsigned long blksz)

Please see my comment to v2 regarding this function.


> +{
> +	int rc = 0;
> +	gpt_header *gpt_h;
> +	gpt_entry *gpt_e;
> +	gpt_header gpt_hdr;
> +	uint32_t calc_crc32;
> +
> +	/* GPT Header starts at "LBA[1]" in the buffer */
> +	gpt_h = buf + blksz;
> +	if (le64_to_cpu(gpt_h->signature) != GPT_HEADER_SIGNATURE) {
> +		printf("%s: 'signature' is invalid\n", __func__);
> +		return -1;
> +	}
> +
> +	memcpy(&gpt_hdr, gpt_h, sizeof(gpt_header));
> +	gpt_hdr.header_crc32 = 0;
> +	calc_crc32 = efi_crc32((const unsigned char *)&gpt_hdr,
> +			       le32_to_cpu(gpt_h->header_size));
> +	if (le32_to_cpu(gpt_h->header_crc32) != calc_crc32) {
> +		printf("%s: 'header_crc32' is invalid\n", __func__);
> +		rc += 1;
> +	}
> +
> +	/* determine start of GPT Entries in the buffer */
> +	gpt_e = buf + (le64_to_cpu(gpt_h->partition_entry_lba) *
> blksz);
> +	calc_crc32 = efi_crc32((const unsigned char *)gpt_e,
> +
> le32_to_cpu(gpt_h->num_partition_entries) *
> +
> le32_to_cpu(gpt_h->sizeof_partition_entry));
> +	if (le32_to_cpu(gpt_h->partition_entry_array_crc32) !=
> calc_crc32) {
> +		printf("%s: 'partition_entry_array_crc32' is
> invalid\n",
> +		       __func__);
> +		rc += 1;
> +	}
> +	return rc;
> +}
> +
> +int write_mbr_and_gpt_partitions(block_dev_desc_t *dev_desc, void
> *buf) +{

I think that this function is redundant since in part_efi.c is already
defined "write_gpt_table()" function.

It shouldn't be difficult to overlay gpt_header and gpt_entry pointers
on the buf.

> +	gpt_header *gpt_h;
> +	gpt_entry *gpt_e;
> +	int gpt_e_blk_cnt;
> +	uint32_t calc_crc32;
> +	uint64_t val;
> +	lbaint_t lba;
> +	int cnt;
> +
> +	if (is_valid_gpt_buf(buf, dev_desc->blksz))
> +		return -1;
> +
> +	/* GPT Header starts at "LBA[1]" in the buffer */
> +	gpt_h = buf + dev_desc->blksz;
> +
> +	/* determine start of GPT Entries in the buffer */
> +	gpt_e = buf + (le64_to_cpu(gpt_h->partition_entry_lba) *
> +		       dev_desc->blksz);
> +	gpt_e_blk_cnt =
> BLOCK_CNT((le32_to_cpu(gpt_h->num_partition_entries) *
> +
> le32_to_cpu(gpt_h->sizeof_partition_entry)),
> +				  dev_desc);
> +
> +	/* write MBR plus Primary GPT */
> +	lba = 0;	/* MBR is always at 0 */
> +	cnt = 2;	/* MBR + GPT Header (2 blocks) */
> +	if (dev_desc->block_write(dev_desc->dev, lba, cnt, buf) !=
> cnt) {
> +		printf("%s: failed writing '%s' (%d blks at 0x" LBAF
> ")\n",
> +		       __func__, "MBR + Primary GPT Header", cnt,
> lba);
> +		return 1;
> +	}
> +
> +	lba = le64_to_cpu(gpt_h->partition_entry_lba);
> +	cnt = gpt_e_blk_cnt;
> +	if (dev_desc->block_write(dev_desc->dev, lba, cnt, gpt_e) !=
> cnt) {
> +		printf("%s: failed writing '%s' (%d blks at 0x" LBAF
> ")\n",
> +		       __func__, "Primary GPT Entries", cnt, lba);
> +		return 1;
> +	}
> +
> +	/* recalculate the values for the Backup GPT Header */
> +	val = le64_to_cpu(gpt_h->my_lba);
> +	gpt_h->my_lba = gpt_h->alternate_lba;
> +	gpt_h->alternate_lba = cpu_to_le64(val);
> +	gpt_h->header_crc32 = 0;
> +
> +	calc_crc32 = efi_crc32((const unsigned char *)gpt_h,
> +			       le32_to_cpu(gpt_h->header_size));
> +	gpt_h->header_crc32 = cpu_to_le32(calc_crc32);
> +
> +	/* write Backup GPT */
> +	lba = le64_to_cpu(gpt_h->last_usable_lba) + 1;
> +	cnt = gpt_e_blk_cnt;
> +	if (dev_desc->block_write(dev_desc->dev, lba, cnt, gpt_e) !=
> cnt) {
> +		printf("%s: failed writing '%s' (%d blks at 0x" LBAF
> ")\n",
> +		       __func__, "Backup GPT Entries", cnt, lba);
> +		return 1;
> +	}
> +
> +	lba = le64_to_cpu(gpt_h->my_lba);
> +	cnt = 1;	/* GPT Header (1 block) */
> +	if (dev_desc->block_write(dev_desc->dev, lba, cnt, gpt_h) !=
> cnt) {
> +		printf("%s: failed writing '%s' (%d blks at 0x" LBAF
> ")\n",
> +		       __func__, "Backup GPT Header", cnt, lba);
> +		return 1;
> +	}
> +
> +	return 0;
> +}
>  #endif
>  
>  /*
> diff --git a/include/part.h b/include/part.h
> index a496a4a..fee1c2d 100644
> --- a/include/part.h
> +++ b/include/part.h
> @@ -244,6 +244,26 @@ int gpt_fill_header(block_dev_desc_t *dev_desc,
> gpt_header *gpt_h, */
>  int gpt_restore(block_dev_desc_t *dev_desc, char *str_disk_guid,
>  		disk_partition_t *partitions, const int parts_count);
> +
> +/**
> + * is_valid_gpt_buf() - Ensure that the Primary GPT information is
> valid
> + *
> + * @param buf - buffer which contains the MBR and Primary GPT info
> + * @param blksz -  block size (in bytes)
> + *
> + * @return - '0' on success, otherwise error
> + */
> +int is_valid_gpt_buf(void *buf, unsigned long blksz);
> +
> +/**
> + * write_mbr_and_gpt_partitions() - write MBR, Primary GPT and
> Backup GPT
> + *
> + * @param dev_desc - block device descriptor
> + * @param buf - buffer which contains the MBR and Primary GPT info
> + *
> + * @return - '0' on success, otherwise error
> + */
> +int write_mbr_and_gpt_partitions(block_dev_desc_t *dev_desc, void
> *buf); #endif
>  
>  #endif /* _PART_H */
diff mbox

Patch

diff --git a/README b/README
index 4ca04d0..42ece99 100644
--- a/README
+++ b/README
@@ -1773,6 +1773,15 @@  The following options need to be configured:
 		regarding the non-volatile storage device. Define this to
 		the eMMC device that fastboot should use to store the image.
 
+		CONFIG_FASTBOOT_GPT_NAME
+		The fastboot "flash" command supports writing the downloaded
+		image to the Protective MBR and the Primary GUID Partition
+		Table. (Additionally, this downloaded image is post-processed
+		to generate and write the Backup GUID Partition Table.)
+		This occurs when the specified "partition name" on the
+		"fastboot flash" command line matches this value.
+		Default is GPT_ENTRY_NAME (currently "gpt") if undefined.
+
 - Journaling Flash filesystem support:
 		CONFIG_JFFS2_NAND, CONFIG_JFFS2_NAND_OFF, CONFIG_JFFS2_NAND_SIZE,
 		CONFIG_JFFS2_NAND_DEV
diff --git a/common/fb_mmc.c b/common/fb_mmc.c
index fb06d8a..5acc8df 100644
--- a/common/fb_mmc.c
+++ b/common/fb_mmc.c
@@ -4,12 +4,17 @@ 
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
+#include <config.h>
 #include <common.h>
 #include <fb_mmc.h>
 #include <part.h>
 #include <aboot.h>
 #include <sparse_format.h>
 
+#ifndef CONFIG_FASTBOOT_GPT_NAME
+#define CONFIG_FASTBOOT_GPT_NAME GPT_ENTRY_NAME
+#endif
+
 /* The 64 defined bytes plus the '\0' */
 #define RESPONSE_LEN	(64 + 1)
 
@@ -62,7 +67,6 @@  static void write_raw_image(block_dev_desc_t *dev_desc, disk_partition_t *info,
 void fb_mmc_flash_write(const char *cmd, void *download_buffer,
 			unsigned int download_bytes, char *response)
 {
-	int ret;
 	block_dev_desc_t *dev_desc;
 	disk_partition_t info;
 
@@ -76,8 +80,24 @@  void fb_mmc_flash_write(const char *cmd, void *download_buffer,
 		return;
 	}
 
-	ret = get_partition_info_efi_by_name(dev_desc, cmd, &info);
-	if (ret) {
+	if (strcmp(cmd, CONFIG_FASTBOOT_GPT_NAME) == 0) {
+		printf("%s: updating MBR, Primary and Backup GPT(s)\n",
+		       __func__);
+		if (is_valid_gpt_buf(download_buffer, dev_desc->blksz)) {
+			printf("%s: invalid GPT - refusing to write to flash\n",
+			       __func__);
+			fastboot_fail("invalid GPT partition");
+			return;
+		}
+		if (write_mbr_and_gpt_partitions(dev_desc, download_buffer)) {
+			printf("%s: writing GPT partitions failed\n", __func__);
+			fastboot_fail("writing GPT partitions failed");
+			return;
+		}
+		printf("........ success\n");
+		fastboot_okay("");
+		return;
+	} else if (get_partition_info_efi_by_name(dev_desc, cmd, &info)) {
 		error("cannot find partition: '%s'\n", cmd);
 		fastboot_fail("cannot find partition");
 		return;
diff --git a/disk/part_efi.c b/disk/part_efi.c
index efed58f..9cc04da 100644
--- a/disk/part_efi.c
+++ b/disk/part_efi.c
@@ -455,6 +455,113 @@  err:
 	free(gpt_h);
 	return ret;
 }
+
+int is_valid_gpt_buf(void *buf, unsigned long blksz)
+{
+	int rc = 0;
+	gpt_header *gpt_h;
+	gpt_entry *gpt_e;
+	gpt_header gpt_hdr;
+	uint32_t calc_crc32;
+
+	/* GPT Header starts at "LBA[1]" in the buffer */
+	gpt_h = buf + blksz;
+	if (le64_to_cpu(gpt_h->signature) != GPT_HEADER_SIGNATURE) {
+		printf("%s: 'signature' is invalid\n", __func__);
+		return -1;
+	}
+
+	memcpy(&gpt_hdr, gpt_h, sizeof(gpt_header));
+	gpt_hdr.header_crc32 = 0;
+	calc_crc32 = efi_crc32((const unsigned char *)&gpt_hdr,
+			       le32_to_cpu(gpt_h->header_size));
+	if (le32_to_cpu(gpt_h->header_crc32) != calc_crc32) {
+		printf("%s: 'header_crc32' is invalid\n", __func__);
+		rc += 1;
+	}
+
+	/* determine start of GPT Entries in the buffer */
+	gpt_e = buf + (le64_to_cpu(gpt_h->partition_entry_lba) * blksz);
+	calc_crc32 = efi_crc32((const unsigned char *)gpt_e,
+			       le32_to_cpu(gpt_h->num_partition_entries) *
+			       le32_to_cpu(gpt_h->sizeof_partition_entry));
+	if (le32_to_cpu(gpt_h->partition_entry_array_crc32) != calc_crc32) {
+		printf("%s: 'partition_entry_array_crc32' is invalid\n",
+		       __func__);
+		rc += 1;
+	}
+	return rc;
+}
+
+int write_mbr_and_gpt_partitions(block_dev_desc_t *dev_desc, void *buf)
+{
+	gpt_header *gpt_h;
+	gpt_entry *gpt_e;
+	int gpt_e_blk_cnt;
+	uint32_t calc_crc32;
+	uint64_t val;
+	lbaint_t lba;
+	int cnt;
+
+	if (is_valid_gpt_buf(buf, dev_desc->blksz))
+		return -1;
+
+	/* GPT Header starts at "LBA[1]" in the buffer */
+	gpt_h = buf + dev_desc->blksz;
+
+	/* determine start of GPT Entries in the buffer */
+	gpt_e = buf + (le64_to_cpu(gpt_h->partition_entry_lba) *
+		       dev_desc->blksz);
+	gpt_e_blk_cnt = BLOCK_CNT((le32_to_cpu(gpt_h->num_partition_entries) *
+				   le32_to_cpu(gpt_h->sizeof_partition_entry)),
+				  dev_desc);
+
+	/* write MBR plus Primary GPT */
+	lba = 0;	/* MBR is always at 0 */
+	cnt = 2;	/* MBR + GPT Header (2 blocks) */
+	if (dev_desc->block_write(dev_desc->dev, lba, cnt, buf) != cnt) {
+		printf("%s: failed writing '%s' (%d blks at 0x" LBAF ")\n",
+		       __func__, "MBR + Primary GPT Header", cnt, lba);
+		return 1;
+	}
+
+	lba = le64_to_cpu(gpt_h->partition_entry_lba);
+	cnt = gpt_e_blk_cnt;
+	if (dev_desc->block_write(dev_desc->dev, lba, cnt, gpt_e) != cnt) {
+		printf("%s: failed writing '%s' (%d blks at 0x" LBAF ")\n",
+		       __func__, "Primary GPT Entries", cnt, lba);
+		return 1;
+	}
+
+	/* recalculate the values for the Backup GPT Header */
+	val = le64_to_cpu(gpt_h->my_lba);
+	gpt_h->my_lba = gpt_h->alternate_lba;
+	gpt_h->alternate_lba = cpu_to_le64(val);
+	gpt_h->header_crc32 = 0;
+
+	calc_crc32 = efi_crc32((const unsigned char *)gpt_h,
+			       le32_to_cpu(gpt_h->header_size));
+	gpt_h->header_crc32 = cpu_to_le32(calc_crc32);
+
+	/* write Backup GPT */
+	lba = le64_to_cpu(gpt_h->last_usable_lba) + 1;
+	cnt = gpt_e_blk_cnt;
+	if (dev_desc->block_write(dev_desc->dev, lba, cnt, gpt_e) != cnt) {
+		printf("%s: failed writing '%s' (%d blks at 0x" LBAF ")\n",
+		       __func__, "Backup GPT Entries", cnt, lba);
+		return 1;
+	}
+
+	lba = le64_to_cpu(gpt_h->my_lba);
+	cnt = 1;	/* GPT Header (1 block) */
+	if (dev_desc->block_write(dev_desc->dev, lba, cnt, gpt_h) != cnt) {
+		printf("%s: failed writing '%s' (%d blks at 0x" LBAF ")\n",
+		       __func__, "Backup GPT Header", cnt, lba);
+		return 1;
+	}
+
+	return 0;
+}
 #endif
 
 /*
diff --git a/include/part.h b/include/part.h
index a496a4a..fee1c2d 100644
--- a/include/part.h
+++ b/include/part.h
@@ -244,6 +244,26 @@  int gpt_fill_header(block_dev_desc_t *dev_desc, gpt_header *gpt_h,
  */
 int gpt_restore(block_dev_desc_t *dev_desc, char *str_disk_guid,
 		disk_partition_t *partitions, const int parts_count);
+
+/**
+ * is_valid_gpt_buf() - Ensure that the Primary GPT information is valid
+ *
+ * @param buf - buffer which contains the MBR and Primary GPT info
+ * @param blksz -  block size (in bytes)
+ *
+ * @return - '0' on success, otherwise error
+ */
+int is_valid_gpt_buf(void *buf, unsigned long blksz);
+
+/**
+ * write_mbr_and_gpt_partitions() - write MBR, Primary GPT and Backup GPT
+ *
+ * @param dev_desc - block device descriptor
+ * @param buf - buffer which contains the MBR and Primary GPT info
+ *
+ * @return - '0' on success, otherwise error
+ */
+int write_mbr_and_gpt_partitions(block_dev_desc_t *dev_desc, void *buf);
 #endif
 
 #endif /* _PART_H */