fastboot: Support defining raw partitions without a partition table

Add support for defining raw fastboot partitions in eMMC by specifying
the offset and size in an environment variable. Optionally, the eMMC
hardware partition number may also be specified.

This makes it possible to e.g. update only part of the eMMC boot
partition, instead of having to write the entire partition.

Signed-off-by: Filip Brozovic <fbrozovic@gmail.com>
diff --git a/drivers/fastboot/fb_mmc.c b/drivers/fastboot/fb_mmc.c
index b2f8932..ae8e8e5 100644
--- a/drivers/fastboot/fb_mmc.c
+++ b/drivers/fastboot/fb_mmc.c
@@ -50,6 +50,48 @@
 	return ret;
 }
 
+static int raw_part_get_info_by_name(struct blk_desc *dev_desc,
+		const char *name, struct disk_partition *info, int *mmcpart)
+{
+	/* strlen("fastboot_raw_partition_") + PART_NAME_LEN + 1 */
+	char env_desc_name[23 + PART_NAME_LEN + 1];
+	char *raw_part_desc;
+	const char *argv[2];
+	const char **parg = argv;
+
+	/* check for raw partition descriptor */
+	strcpy(env_desc_name, "fastboot_raw_partition_");
+	strncat(env_desc_name, name, PART_NAME_LEN);
+	raw_part_desc = strdup(env_get(env_desc_name));
+	if (raw_part_desc == NULL)
+		return -ENODEV;
+
+	/*
+	 * parse partition descriptor
+	 *
+	 * <lba_start> <lba_size> [mmcpart <num>]
+	 */
+	for (; parg < argv + sizeof(argv) / sizeof(*argv); ++parg) {
+		*parg = strsep(&raw_part_desc, " ");
+		if (*parg == NULL) {
+			pr_err("Invalid number of arguments.\n");
+			return -ENODEV;
+		}
+	}
+
+	info->start = simple_strtoul(argv[0], NULL, 0);
+	info->size = simple_strtoul(argv[1], NULL, 0);
+	info->blksz = dev_desc->blksz;
+	strncpy((char *)info->name, name, PART_NAME_LEN);
+
+	if (raw_part_desc) {
+		if (strcmp(strsep(&raw_part_desc, " "), "mmcpart") == 0)
+			*mmcpart = simple_strtoul(raw_part_desc, NULL, 0);
+	}
+
+	return 0;
+}
+
 /**
  * fb_mmc_blk_write() - Write/erase MMC in chunks of FASTBOOT_MAX_BLK_WRITE
  *
@@ -376,7 +418,8 @@
 			       struct blk_desc **dev_desc,
 			       struct disk_partition *part_info, char *response)
 {
-	int r;
+	int r = 0;
+	int mmcpart;
 
 	*dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
 	if (!*dev_desc) {
@@ -388,10 +431,12 @@
 		return -ENOENT;
 	}
 
-	r = part_get_info_by_name_or_alias(*dev_desc, part_name, part_info);
-	if (r < 0) {
-		fastboot_fail("partition not found", response);
-		return r;
+	if (raw_part_get_info_by_name(*dev_desc, part_name, part_info, &mmcpart) < 0) {
+		r = part_get_info_by_name_or_alias(*dev_desc, part_name, part_info);
+		if (r < 0) {
+			fastboot_fail("partition not found", response);
+			return r;
+		}
 	}
 
 	return r;
@@ -410,6 +455,7 @@
 {
 	struct blk_desc *dev_desc;
 	struct disk_partition info;
+	int mmcpart = 0;
 
 	dev_desc = blk_get_dev("mmc", CONFIG_FASTBOOT_FLASH_MMC_DEV);
 	if (!dev_desc || dev_desc->type == DEV_TYPE_UNKNOWN) {
@@ -482,7 +528,13 @@
 	}
 #endif
 
-	if (part_get_info_by_name_or_alias(dev_desc, cmd, &info) < 0) {
+	if (raw_part_get_info_by_name(dev_desc, cmd, &info, &mmcpart) == 0) {
+		if (blk_dselect_hwpart(dev_desc, mmcpart)) {
+			pr_err("Failed to select hwpart\n");
+			fastboot_fail("Failed to select hwpart", 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;
@@ -524,11 +576,11 @@
  */
 void fastboot_mmc_erase(const char *cmd, char *response)
 {
-	int ret;
 	struct blk_desc *dev_desc;
 	struct disk_partition info;
 	lbaint_t blks, blks_start, blks_size, grp_size;
 	struct mmc *mmc = find_mmc_device(CONFIG_FASTBOOT_FLASH_MMC_DEV);
+	int mmcpart = 0;
 
 	if (mmc == NULL) {
 		pr_err("invalid mmc device\n");
@@ -562,8 +614,13 @@
 	}
 #endif
 
-	ret = part_get_info_by_name_or_alias(dev_desc, cmd, &info);
-	if (ret < 0) {
+	if (raw_part_get_info_by_name(dev_desc, cmd, &info, &mmcpart) == 0) {
+		if (blk_dselect_hwpart(dev_desc, mmcpart)) {
+			pr_err("Failed to select hwpart\n");
+			fastboot_fail("Failed to select hwpart", 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;