[U-Boot] [PATCH] fastboot: mmc: add support for flashing boot partitions

Neil Armstrong narmstrong at baylibre.com
Fri Jun 28 09:24:22 UTC 2019


Hi Fabien,

On 25/06/2019 18:43, Fabien Parent wrote:
> MMC storage supports 4 hardware partitions:
> 	* User partition
> 	* 2 boot partitions
> 	* RPMB partition
> 
> Flashing to the User partition is already supported, and this commit
> adds support for flashing to the 2 boot partitions.

I find it quite dansgerous to expose the whole boot0/boot1 partitions
via fastboot, usually a bootloader or environment will use only a small
area of these HW partitions, like SPI or other non volatile storages.

I implemented a way to handle "vendor" partitions instead since bootloader.
envs, ... on boot HW partitions is heavily vendor/board specific.

Here is the fastboot code I added :
=======><===========================================
diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
index d63ecdd27e..9c914d4842 100644
--- a/drivers/fastboot/Kconfig
+++ b/drivers/fastboot/Kconfig
@@ -86,6 +86,10 @@ config FASTBOOT_FLASH_NAND

 endchoice

+config FASTBOOT_FLASH_CUSTOM
+	bool "FASTBOOT on custom board partitions"
+	depends on FASTBOOT_FLASH
+
 config FASTBOOT_FLASH_MMC_DEV
 	int "Define FASTBOOT MMC FLASH default device"
 	depends on FASTBOOT_FLASH_MMC
diff --git a/drivers/fastboot/fb_command.c b/drivers/fastboot/fb_command.c
index 200f9910c5..57e14cdbde 100644
--- a/drivers/fastboot/fb_command.c
+++ b/drivers/fastboot/fb_command.c
@@ -8,6 +8,7 @@
 #include <fastboot-internal.h>
 #include <fb_mmc.h>
 #include <fb_nand.h>
+#include <fb_board.h>
 #include <part.h>
 #include <stdlib.h>

@@ -266,6 +267,11 @@ void fastboot_data_complete(char *response)
  */
 static void flash(char *cmd_parameter, char *response)
 {
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_CUSTOM)
+	if (fastboot_board_flash_write(cmd_parameter, fastboot_buf_addr,
+				       image_size, response))
+		return;
+#endif
 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
 	fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size,
 				 response);
@@ -287,6 +293,10 @@ static void flash(char *cmd_parameter, char *response)
  */
 static void erase(char *cmd_parameter, char *response)
 {
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_CUSTOM)
+	if (fastboot_board_erase(cmd_parameter, response))
+		return;
+#endif
 #if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC)
 	fastboot_mmc_erase(cmd_parameter, response);
 #endif
diff --git a/include/fb_board.h b/include/fb_board.h
new file mode 100644
index 0000000000..c5d6f59fc6
--- /dev/null
+++ b/include/fb_board.h
@@ -0,0 +1,28 @@
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ * Copyright 2019 BayLibre SAS
+ */
+
+#ifndef _FB_BOARD_H_
+#define _FB_BOARD_H_
+
+/**
+ * fastboot_board_flash_write() - Write image to board partition for fastboot
+ *
+ * @cmd: Named partition to write image to
+ * @download_buffer: Pointer to image data
+ * @download_bytes: Size of image data
+ * @response: Pointer to fastboot response buffer
+ * @return true if write was handled, false if was not handled
+ */
+bool fastboot_board_flash_write(const char *cmd, void *download_buffer,
+				u32 download_bytes, char *response);
+/**
+ * fastboot_board_flash_erase() - Erase board partition for fastboot
+ *
+ * @cmd: Named partition to erase
+ * @response: Pointer to fastboot response buffer
+ * @return true if erase was handled, false if was not handled
+ */
+bool fastboot_board_erase(const char *cmd, char *response);
+#endif
========><=============================================================

This could handle any virtual partitions on any device (SPI, eeprom, mmc HW, eFuses, NVMEM.....)
is a generic way from the fastboot code, but protecting the whole memory zone from being
re-written if it contains sensible data like factory data, keys, ....

Then you can add the following (not optimal at all) in a board support :
========><=============================================================
+#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_CUSTOM) && CONFIG_IS_ENABLED(MMC_WRITE)
+
+#include <malloc.h>
+#include <blk.h>
+#include <fastboot.h>
+#include <fastboot-internal.h>
+#include <fb_board.h>
+#include <image-sparse.h>
+#include <mmc.h>
+#include <div64.h>
+#include <linux/compat.h>
+
+#define FASBOOT_HWPART_MMC_ENV_DEV		CONFIG_SYS_MMC_ENV_DEV
+#define FASBOOT_HWPART_MMC_ENV_PART		CONFIG_SYS_MMC_ENV_PART
+#define FASBOOT_HWPART_BOOTLOADER_OFFSET 	0x200	/* Block 1 */
+#define FASBOOT_HWPART_BOOTLOADER_LENGTH 	0xffe00	/* 1MiB */
+#define FASBOOT_HWPART_BOOTENV_OFFSET		CONFIG_ENV_OFFSET
+#define FASBOOT_HWPART_BOOTENV_LENGTH		CONFIG_ENV_SIZE
+
+#define FASTBOOT_MAX_BLK_WRITE 16384
+
+static bool fastboot_board_hwpart_process(const char *cmd, void *buffer,
+					  u32 bytes, char *response)
+{
+	struct blk_desc *desc;
+	void *blk_ptr = NULL;
+	int offset, length;
+	struct mmc *mmc;
+	lbaint_t blkcnt;
+	lbaint_t blk;
+	lbaint_t blks_written;
+	lbaint_t cur_blkcnt;
+	lbaint_t blks = 0;
+	int ret, i;
+
+	/* Check if we handle these partitions */
+	if (strcmp(cmd, "bootloader") == 0) {
+		offset = FASBOOT_HWPART_BOOTLOADER_OFFSET;
+		length = FASBOOT_HWPART_BOOTLOADER_LENGTH;
+	} else if (strcmp(cmd, "bootenv") == 0) {
+		offset = FASBOOT_HWPART_BOOTENV_OFFSET;
+		length = FASBOOT_HWPART_BOOTENV_LENGTH;
+	} else
+		return false;
+
+	if (buffer && is_sparse_image(buffer)) {
+		pr_err("sparse buffer not supported");
+		fastboot_fail("sparse buffer not supported", response);
+		return true;
+	}
+
+	mmc = find_mmc_device(FASBOOT_HWPART_MMC_ENV_DEV);
+	if (!mmc) {
+		pr_err("invalid mmc device\n");
+		fastboot_fail("invalid mmc device", response);
+		return true;
+	}
+
+	ret = mmc_switch_part(mmc, FASBOOT_HWPART_MMC_ENV_PART);
+	if (ret) {
+		pr_err("invalid mmc hwpart\n");
+		fastboot_fail("invalid mmc hwpart", response);
+		return true;
+	}
+
+	desc = mmc_get_blk_desc(mmc);
+
+	if (offset < 0)
+		offset += mmc->capacity;
+
+	printf("using custom hwpart %s at offset 0x%x and length %d bytes\n",
+	       cmd, offset, length);
+
+	/* Use full length in erase mode */
+	if (!buffer)
+		bytes = length;
+
+	/* determine number of blocks to write */
+	blkcnt = ((bytes + (desc->blksz - 1)) & ~(desc->blksz - 1));
+	blkcnt = lldiv(blkcnt, desc->blksz);
+
+	if ((blkcnt * desc->blksz) > length) {
+		pr_err("too large for partition: '%s'\n", cmd);
+		fastboot_fail("too large for partition", response);
+		return true;
+	}
+
+	blk = offset / desc->blksz;
+
+	printf("writing " LBAFU " blocks starting at %ld...\n", blkcnt, blk);
+
+	for (i = 0; i < blkcnt; i += FASTBOOT_MAX_BLK_WRITE) {
+		cur_blkcnt = min((int)blkcnt - i, FASTBOOT_MAX_BLK_WRITE);
+		if (buffer) {
+			if (fastboot_progress_callback)
+				fastboot_progress_callback("writing");
+			blks_written = blk_dwrite(desc, blk, cur_blkcnt,
+						  buffer + (i * desc->blksz));
+		} else if ((blk % mmc->erase_grp_size) == 0 &&
+			   (cur_blkcnt % mmc->erase_grp_size) == 0) {
+			/* Only call erase if multiple of erase group */
+			if (fastboot_progress_callback)
+				fastboot_progress_callback("erasing");
+			blks_written = blk_derase(desc, blk, cur_blkcnt);
+		} else {
+			/* Write 0s if not aligned */
+			if (!blk_ptr) {
+				blk_ptr = malloc(desc->blksz);
+				if (!blk_ptr) {
+					pr_err("failed to allocate\n");
+					fastboot_fail("failed to allocate",
+						      response);
+					return true;
+				}
+				memset(blk_ptr, 0, desc->blksz);
+			}
+			if (fastboot_progress_callback)
+				fastboot_progress_callback("erasing");
+			blks_written = 0;
+			do {
+				blks_written += blk_dwrite(desc, blk, 1,
+							   blk_ptr);
+			} while (--cur_blkcnt);
+		}
+		blk += blks_written;
+		blks += blks_written;
+	}
+
+	if (blk_ptr)
+		free(blk_ptr);
+
+	if (blks != blkcnt) {
+		pr_err("failed writing to device %d\n", desc->devnum);
+		fastboot_fail("failed writing to device", response);
+		return true;
+	}
+
+	printf("........ wrote " LBAFU " bytes to '%s'\n", blkcnt * desc->blksz,
+	       cmd);
+	fastboot_okay(NULL, response);
+
+	return true;
+}
+
+
+bool fastboot_board_flash_write(const char *cmd, void *download_buffer,
+				u32 download_bytes, char *response)
+{
+	return fastboot_board_hwpart_process(cmd, download_buffer,
+					     download_bytes, response);
+}
+
+bool fastboot_board_erase(const char *cmd, char *response)
+{
+	return fastboot_board_hwpart_process(cmd, NULL, 0, response);
+}
+#endif
========><=============================================================

> 
> User can run the following commands to flash to the boot partitions:
> 	fastboot flash mmcboot0 myfile
> 	fastboot flash mmcboot1 myfile
> 
> "mmcboot" is the default name of the partition and can be changed via
> a Kconfig option.
> 
> Signed-off-by: Fabien Parent <fparent at baylibre.com>
> ---
>  drivers/fastboot/Kconfig  | 17 +++++++++++++++++
>  drivers/fastboot/fb_mmc.c | 36 +++++++++++++++++++++++++++++++++++-
>  2 files changed, 52 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/fastboot/Kconfig b/drivers/fastboot/Kconfig
> index d63ecdd27e..f481d7596c 100644
> --- a/drivers/fastboot/Kconfig
> +++ b/drivers/fastboot/Kconfig
> @@ -126,6 +126,23 @@ config FASTBOOT_MBR_NAME
>  	  specified on the "fastboot flash" command line matches the value
>  	  defined here. The default target name for updating MBR is "mbr".
>  
> +config FASTBOOT_MMC_BOOT_PART_NAME
> +	string "Target name for updating MMC's boot partitions"
> +	depends on FASTBOOT_FLASH_MMC
> +	default "mmcboot"
> +	help
> +	  The fastboot "flash" command supports writing the downloaded
> +	  image to the MMC's boot partitions.
> +	  This occurs when the specified "partition name" on the
> +	  "fastboot flash" command line is starts with the value defined here,
> +	  and ends with '0' or '1' to specify which boot partition is being
> +	  targeted.
> +	  The default name for updating the boot partitions is "mmcboot".
> +	  For example the two partitions can be flashed using the following
> +	  commands if the default name is used:
> +	    - "fastboot flash mmcboot0 myfile",
> +	    - "fastboot flash mmcboot1 myfile".
> +
>  config FASTBOOT_CMD_OEM_FORMAT
>  	bool "Enable the 'oem format' command"
>  	depends on FASTBOOT_FLASH_MMC && CMD_GPT
> diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c
> index 90ca81da9b..c2451200f7 100644
> --- a/drivers/fastboot/fb_mmc.c
> +++ b/drivers/fastboot/fb_mmc.c
> @@ -46,6 +46,28 @@ static int part_get_info_by_name_or_alias(struct blk_desc *dev_desc,
>  	return ret;
>  }
>  
> +static int part_get_mmc_boot_info(int hwpart, disk_partition_t *info)
> +{
> +	struct mmc *mmc;
> +	int ret;
> +
> +	mmc = find_mmc_device(CONFIG_FASTBOOT_FLASH_MMC_DEV);
> +	if (!mmc)
> +		return -EINVAL;
> +
> +	ret = blk_select_hwpart_devnum(IF_TYPE_MMC,
> +				       CONFIG_FASTBOOT_FLASH_MMC_DEV, hwpart);
> +	if (ret < 0)
> +		return ret;
> +
> +	memset(info, 0, sizeof(*info));
> +	info->start = 0;
> +	info->size = mmc->capacity_boot;
> +	info->blksz = mmc->write_bl_len;
> +
> +	return 0;
> +}
> +
>  /**
>   * fb_mmc_blk_write() - Write/erase MMC in chunks of FASTBOOT_MAX_BLK_WRITE
>   *
> @@ -394,7 +416,19 @@ void fastboot_mmc_flash_write(const char *cmd, void *download_buffer,
>  	}
>  #endif
>  
> -	if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) {
> +	if (strcmp(cmd, CONFIG_FASTBOOT_MMC_BOOT_PART_NAME "0") == 0) {
> +		if (part_get_mmc_boot_info(1, &info) < 0) {
> +			pr_err("cannot find partition: '%s'\n", cmd);
> +			fastboot_fail("cannot find partition", response);
> +			return;
> +		}
> +	} else if (strcmp(cmd, CONFIG_FASTBOOT_MMC_BOOT_PART_NAME "1") == 0) {
> +		if (part_get_mmc_boot_info(2, &info) < 0) {
> +			pr_err("cannot find partition: '%s'\n", cmd);
> +			fastboot_fail("cannot find partition", response);
> +			return;
> +		}
> +	} else if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) {
>  		pr_err("cannot find partition: '%s'\n", cmd);
>  		fastboot_fail("cannot find partition", response);
>  		return;
> 



More information about the U-Boot mailing list