ANDROID: Add "boot_android" command.

The new "boot_android" command simply executes the Android Bootloader
flow. This receives the location (interface, dev, partition) of the
Android "misc" partition which is then used to lookup and infer the
kernel and system images that should be booted.

Bug: 32707546
Bug: 141272741
Test: Booted a rpi3 build with Android Things.
Change-Id: I2647ebb3b78bc00629e765a10719c5aee753670d
Signed-off-by: Alex Deymo <deymo@google.com>
Signed-off-by: Alistair Strachan <astrachan@google.com>
[dvander: renamed fdt_addr to fdtaddr]
Signed-off-by: David Anderson <dvander@google.com>

Link: https://android-review.googlesource.com/c/platform/external/u-boot/+/1126444/7
Bug: https://baylibre.atlassian.net/browse/RITA-97
Signed-off-by: Mattijs Korpershoek <mkorpershoek@baylibre.com>
diff --git a/README b/README
index bb5e28e..94ddda0 100644
--- a/README
+++ b/README
@@ -1164,6 +1164,12 @@
 		"bootloader", which often means reboot to fastboot but may also
 		include a UI with a menu.
 
+		CONFIG_CMD_BOOT_ANDROID
+		This enables the command "boot_android" which executes the
+		Android Bootloader flow. Enabling CONFIG_CMD_FASTBOOT is
+		recommended to support the Android Fastboot protocol as part
+		of the bootloader.
+
 - Journaling Flash filesystem support:
 		CONFIG_JFFS2_NAND
 		Define these for a default partition on a NAND device
diff --git a/cmd/Kconfig b/cmd/Kconfig
index 0ac4e05..15708f1 100644
--- a/cmd/Kconfig
+++ b/cmd/Kconfig
@@ -1325,6 +1325,16 @@
 	  is used by the new A/B update model where one slot is updated in the
 	  background while running from the other slot.
 
+config CMD_BOOT_ANDROID
+	bool "boot_android"
+	default n
+	depends on ANDROID_BOOTLOADER
+	help
+	  Performs the Android Bootloader boot flow, loading the appropriate
+	  Android image (normal kernel, recovery kernel or "bootloader" mode)
+	  and booting it. The boot mode is determined by the contents of the
+	  Android Bootloader Message.
+
 endmenu
 
 if NET
diff --git a/cmd/Makefile b/cmd/Makefile
index f54e549..818c1be 100644
--- a/cmd/Makefile
+++ b/cmd/Makefile
@@ -25,6 +25,7 @@
 obj-$(CONFIG_CMD_BINOP) += binop.o
 obj-$(CONFIG_CMD_BLOCK_CACHE) += blkcache.o
 obj-$(CONFIG_CMD_BMP) += bmp.o
+obj-$(CONFIG_CMD_BOOT_ANDROID) += boot_android.o
 obj-$(CONFIG_CMD_BOOTCOUNT) += bootcount.o
 obj-$(CONFIG_CMD_BOOTEFI) += bootefi.o
 obj-$(CONFIG_CMD_BOOTMENU) += bootmenu.o
diff --git a/cmd/boot_android.c b/cmd/boot_android.c
new file mode 100644
index 0000000..cf7e796
--- /dev/null
+++ b/cmd/boot_android.c
@@ -0,0 +1,74 @@
+// SPDX-License-Identifier: BSD-2-Clause
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ */
+
+#include <android_bootloader.h>
+#include <common.h>
+#include <command.h>
+#include <part.h>
+
+static int do_boot_android(cmd_tbl_t *cmdtp, int flag, int argc,
+			   char * const argv[])
+{
+	unsigned long load_address;
+	int ret = CMD_RET_SUCCESS;
+	char *addr_arg_endp, *addr_str;
+	struct blk_desc *dev_desc;
+	disk_partition_t part_info;
+	const char *misc_part_iface;
+	const char *misc_part_desc;
+
+	if (argc < 4)
+		return CMD_RET_USAGE;
+	if (argc > 5)
+		return CMD_RET_USAGE;
+
+	if (argc >= 5) {
+		load_address = simple_strtoul(argv[4], &addr_arg_endp, 16);
+		if (addr_arg_endp == argv[4] || *addr_arg_endp != '\0')
+			return CMD_RET_USAGE;
+	} else {
+		addr_str = env_get("loadaddr");
+		if (addr_str)
+			load_address = simple_strtoul(addr_str, NULL, 16);
+		else
+			load_address = CONFIG_SYS_LOAD_ADDR;
+	}
+
+	/* Lookup the "misc" partition from argv[1] and argv[2] */
+	misc_part_iface = argv[1];
+	misc_part_desc = argv[2];
+	/* Split the part_name if passed as "$dev_num;part_name". */
+	if (part_get_info_by_dev_and_name_or_num(misc_part_iface,
+						 misc_part_desc,
+						 &dev_desc, &part_info) < 0)
+		return CMD_RET_FAILURE;
+
+	ret = android_bootloader_boot_flow(dev_desc, &part_info, argv[3],
+					   load_address);
+	if (ret < 0) {
+		printf("Android boot failed, error %d.\n", ret);
+		return CMD_RET_FAILURE;
+	}
+	return CMD_RET_SUCCESS;
+}
+
+U_BOOT_CMD(
+	boot_android, 5, 0, do_boot_android,
+	"Execute the Android Bootloader flow.",
+	"<interface> <dev[:part|;part_name]> <slot> [<kernel_addr>]\n"
+	"    - Load the Boot Control Block (BCB) from the partition 'part' on\n"
+	"      device type 'interface' instance 'dev' to determine the boot\n"
+	"      mode, and load and execute the appropriate kernel.\n"
+	"      In normal and recovery mode, the kernel will be loaded from\n"
+	"      the corresponding \"boot\" partition. In bootloader mode, the\n"
+	"      command defined in the \"fastbootcmd\" variable will be\n"
+	"      executed.\n"
+	"      On Android devices with multiple slots, the pass 'slot' is\n"
+	"      used to load the appropriate kernel. The standard slot names\n"
+	"      are 'a' and 'b'.\n"
+	"    - If 'part_name' is passed, preceded with a ; instead of :, the\n"
+	"      partition name whose label is 'part_name' will be looked up in\n"
+	"      the partition table. This is commonly the \"misc\" partition.\n"
+);
diff --git a/common/android_bootloader.c b/common/android_bootloader.c
index cdc77a8..00af3f7 100644
--- a/common/android_bootloader.c
+++ b/common/android_bootloader.c
@@ -126,12 +126,17 @@
 {
 	char *part_name;
 	int part_num;
+	size_t part_name_len;
 
-	part_name = malloc(strlen(base_name) + strlen(slot_suffix) + 1);
+	part_name_len = strlen(base_name) + 1;
+	if (slot_suffix)
+		part_name_len += strlen(slot_suffix);
+	part_name = malloc(part_name_len);
 	if (!part_name)
 		return -1;
 	strcpy(part_name, base_name);
-	strcat(part_name, slot_suffix);
+	if (slot_suffix)
+		strcat(part_name, slot_suffix);
 
 	part_num = part_get_info_by_name(dev_desc, part_name, part_info);
 	if (part_num < 0) {
@@ -155,8 +160,9 @@
 static int android_bootloader_boot_kernel(unsigned long kernel_address)
 {
 	char kernel_addr_str[12];
-	char *fdt_addr = env_get("fdt_addr");
-	char *bootm_args[] = { "bootm", kernel_addr_str, "-", fdt_addr, NULL };
+	char *fdt_addr = env_get("fdtaddr");
+	char *bootm_args[] = {
+		"bootm", kernel_addr_str, kernel_addr_str, fdt_addr, NULL };
 
 	sprintf(kernel_addr_str, "0x%lx", kernel_address);
 
@@ -256,6 +262,7 @@
 
 int android_bootloader_boot_flow(struct blk_desc *dev_desc,
 				 const disk_partition_t *misc_part_info,
+				 const char *slot,
 				 unsigned long kernel_address)
 {
 	enum android_boot_mode mode;
@@ -264,8 +271,7 @@
 	int boot_part_num, system_part_num;
 	int ret;
 	char *command_line;
-	/* TODO: lookup the slot_suffix based on the BCB. */
-	const char *slot_suffix = "_a";
+	char slot_suffix[3];
 	const char *mode_cmdline = NULL;
 
 	/* Determine the boot mode and clear its value for the next boot if
@@ -295,6 +301,13 @@
 		return android_bootloader_boot_bootloader();
 	}
 
+	slot_suffix[0] = '\0';
+	if (slot && slot[0]) {
+		slot_suffix[0] = '_';
+		slot_suffix[1] = slot[0];
+		slot_suffix[2] = '\0';
+	}
+
 	/* Load the kernel from the desired "boot" partition. */
 	boot_part_num =
 	    android_part_get_info_by_name_suffix(dev_desc,
diff --git a/include/android_bootloader.h b/include/android_bootloader.h
index 947913e..ddf6d76 100644
--- a/include/android_bootloader.h
+++ b/include/android_bootloader.h
@@ -33,10 +33,16 @@
  * The boot mode is determined by the contents of the Android Bootloader
  * Message. On success it doesn't return.
  *
+ * @dev_desc:		device where to load the kernel and system to boot from.
+ * @misc_part_info:	the "misc" partition descriptor in 'dev_desc'.
+ * @slot:		the boot slot to boot from.
+ * @kernel_address:	address where to load the kernel if needed.
+ *
  * @return a negative number in case of error, otherwise it doesn't return.
  */
 int android_bootloader_boot_flow(struct blk_desc *dev_desc,
 				 const disk_partition_t *misc_part_info,
+				 const char *slot,
 				 unsigned long kernel_address);
 
 #endif  /* __ANDROID_BOOTLOADER_H */