[gbl] Implement GBL EFI Fastboot protocol for bootloader flashing and EFI execution

Implement the GblEfiFastbootProtocol in U-Boot to allow GBL to delegate
certain fastboot commands to the firmware.

Specifically, implement the command_exec method to handle:
- 'fastboot flash bootloader': Delegates to amlmmc_write_bootloader to
  flash both eMMC boot partitions (boot0/boot1).
- 'fastboot oem run-staged-efi': Executes the staged EFI image from GBL's
  download buffer using U-Boot's bootefi command.

This enables GBL to support bootloader updates and recovery EFI execution
over fastboot.

Bug: 496295142
TAG=agy
CONV=236c2e4e-69e4-48b6-a8ea-8986bc68bb04

Change-Id: I42dc35148d5d52c9860a7f6bd5a44124f3b4eec1
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/1351452
Reviewed-by: Dov Shlachter <dovs@google.com>
Commit-Queue: Sergii Parubochyi <sergiip@google.com>
GitOrigin-RevId: 9c97c64467406120f97c29cc138527d9572145f5
diff --git a/include/efi_loader.h b/include/efi_loader.h
index 17f2175..c77e996 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -581,6 +581,7 @@
 efi_status_t efi_android_boot_register(void);
 efi_status_t efi_gbl_fastboot_transport_register(void);
 efi_status_t efi_rng_sw_register(void);
+efi_status_t efi_gbl_fastboot_register(void);
 /* Called by efi_init_obj_list() to do initial measurement */
 efi_status_t efi_tcg2_do_initial_measurement(void);
 /* measure the pe-coff image, extend PCR and add Event Log */
diff --git a/include/gbl_efi_common.h b/include/gbl_efi_common.h
new file mode 100644
index 0000000..a4ebbe0
--- /dev/null
+++ b/include/gbl_efi_common.h
@@ -0,0 +1,16 @@
+/*
+ * Copyright (c) 2026 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#ifndef __GBL_EFI_COMMON_H__
+#define __GBL_EFI_COMMON_H__
+
+#define GBL_PROTOCOL_REVISION(major, minor) ((((major) & 0xFFFF) << 16) | ((minor) & 0xFFFF))
+
+#define EFI_ENUM(camelname, width, ...) \
+  enum { __VA_ARGS__ };                 \
+  typedef width camelname
+
+#endif /* __GBL_EFI_COMMON_H__ */
diff --git a/include/gbl_efi_fastboot_protocol.h b/include/gbl_efi_fastboot_protocol.h
new file mode 100644
index 0000000..be28fb7
--- /dev/null
+++ b/include/gbl_efi_fastboot_protocol.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (c) 2026 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#ifndef __GBL_EFI_FASTBOOT_PROTOCOL_H__
+#define __GBL_EFI_FASTBOOT_PROTOCOL_H__
+
+#include <efi_api.h>
+#include <gbl_efi_common.h>
+
+#define GBL_EFI_FASTBOOT_PROTOCOL_GUID                                     \
+	EFI_GUID(0xc67e48a0, 0x5eb8, 0x4127, 0xbe, 0x89, 0xdf, 0x2e, 0xd9, \
+		 0x3d, 0x8a, 0x9a)
+
+#define GBL_EFI_FASTBOOT_PROTOCOL_REVISION GBL_PROTOCOL_REVISION(1, 0)
+
+#define GBL_EFI_FASTBOOT_SERIAL_NUMBER_MAX_LEN_UTF8 32
+
+typedef void (*GetVarAllCallback)(void* context, size_t num_args,
+				  const char *const *args,
+				  const char *val);
+
+EFI_ENUM(GblEfiFastbootMessageType, uint32_t,
+	GBL_EFI_FASTBOOT_MESSAGE_TYPE_OKAY,
+	GBL_EFI_FASTBOOT_MESSAGE_TYPE_FAIL,
+	GBL_EFI_FASTBOOT_MESSAGE_TYPE_INFO);
+
+typedef efi_status_t (*FastbootMessageSender)(void* context,
+					   GblEfiFastbootMessageType msg_type,
+					   size_t msg_len, const char *msg);
+
+EFI_ENUM(GblEfiFastbootCommandExecResult, uint32_t,
+	GBL_EFI_FASTBOOT_COMMAND_EXEC_RESULT_PROHIBITED,
+	GBL_EFI_FASTBOOT_COMMAND_EXEC_RESULT_DEFAULT_IMPL,
+	GBL_EFI_FASTBOOT_COMMAND_EXEC_RESULT_CUSTOM_IMPL);
+
+typedef struct GblEfiFastbootProtocol {
+	uint64_t revision;
+	char serial_number[GBL_EFI_FASTBOOT_SERIAL_NUMBER_MAX_LEN_UTF8];
+
+	efi_status_t (*get_var)(struct GblEfiFastbootProtocol *self,
+			     size_t num_args, const char *const *args,
+			     size_t *buffer_size, char *buffer);
+	efi_status_t (*get_var_all)(struct GblEfiFastbootProtocol *self,
+				 void *context, GetVarAllCallback cb);
+
+	efi_status_t (*get_staged)(struct GblEfiFastbootProtocol *self,
+				size_t *buffer_size, size_t *buffer_remains,
+				uint8_t *buffer);
+
+	efi_status_t (*command_exec)(struct GblEfiFastbootProtocol *self,
+				  size_t num_args, const char *const *args,
+				  size_t download_buffer_size,
+				  size_t download_buffer_used_size,
+				  uint8_t *download_buffer,
+				  GblEfiFastbootCommandExecResult *implementation,
+				  FastbootMessageSender sender, void *context);
+	efi_status_t (*get_partition_type)(struct GblEfiFastbootProtocol *self,
+					const char *part_name,
+					size_t *part_type_len, char *part_type);
+} GblEfiFastbootProtocol;
+
+#endif /* __GBL_EFI_FASTBOOT_PROTOCOL_H__ */
diff --git a/include/gbl_efi_fastboot_transport.h b/include/gbl_efi_fastboot_transport.h
index 1f7807c..30dd62c 100644
--- a/include/gbl_efi_fastboot_transport.h
+++ b/include/gbl_efi_fastboot_transport.h
@@ -1,12 +1,13 @@
 /*
- * Copyright (c) 2025 The Fuchsia Authors
+ * Copyright (c) 2026 The Fuchsia Authors
  *
  * SPDX-License-Identifier:	BSD-3-Clause
  */
 
 #include <efi_api.h>
+#include <gbl_efi_common.h>
 
-#define GBL_EFI_FASTBOOT_TRANSPORT_PROTOCOL_REVISION 0x00000000
+#define GBL_EFI_FASTBOOT_TRANSPORT_PROTOCOL_REVISION GBL_PROTOCOL_REVISION(1, 0)
 
 #define GBL_EFI_FASTBOOT_TRANSPORT_PROTOCOL_GUID                           \
 	EFI_GUID(0xedade92c, 0x5c48, 0x440d, 0x84, 0x9c, 0xe2, 0xa0, 0xc7, \
@@ -20,12 +21,12 @@
 typedef struct GblEfiFastbootTransportProtocol {
 	uint64_t revision;
 	const char *description;
-	EfiStatus (*start)(struct GblEfiFastbootTransportProtocol *self);
-	EfiStatus (*stop)(struct GblEfiFastbootTransportProtocol *self);
-	EfiStatus (*receive)(struct GblEfiFastbootTransportProtocol *self,
+	efi_status_t (*start)(struct GblEfiFastbootTransportProtocol *self);
+	efi_status_t (*stop)(struct GblEfiFastbootTransportProtocol *self);
+	efi_status_t (*receive)(struct GblEfiFastbootTransportProtocol *self,
 			     size_t *buffer_size, void *buffer,
 			     GblEfiFastbootRxMode mode);
-	EfiStatus (*send)(struct GblEfiFastbootTransportProtocol *self,
+	efi_status_t (*send)(struct GblEfiFastbootTransportProtocol *self,
 			  size_t *buffer_size, const void *buffer);
-	EfiStatus (*flush)(struct GblEfiFastbootTransportProtocol *self);
+	efi_status_t (*flush)(struct GblEfiFastbootTransportProtocol *self);
 } GblEfiFastbootTransportProtocol;
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 4d6d1ca..e1933ee 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -92,6 +92,7 @@
 
 obj-y += efi_android_boot.o
 obj-y += gbl_efi_fastboot_transport.o
+obj-y += gbl_efi_fastboot.o
 obj-y += efi_aml_sd_emmc.o
 obj-y += efi_timestamp.o
 
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index 1bdb868..c5696af 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -318,6 +318,10 @@
 	if (ret != EFI_SUCCESS)
 		goto out;
 
+	ret = efi_gbl_fastboot_register();
+	if (ret != EFI_SUCCESS)
+		goto out;
+
 	/* Secure boot */
 	ret = efi_init_secure_boot();
 	if (ret != EFI_SUCCESS)
diff --git a/lib/efi_loader/gbl_efi_fastboot.c b/lib/efi_loader/gbl_efi_fastboot.c
new file mode 100644
index 0000000..c32d066
--- /dev/null
+++ b/lib/efi_loader/gbl_efi_fastboot.c
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2026 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <gbl_efi_fastboot_protocol.h>
+#include <mmc.h>
+#include <amlogic/aml_mmc.h>
+
+static efi_status_t EFIAPI get_var(struct GblEfiFastbootProtocol *self,
+				   size_t num_args, const char *const *args,
+				   size_t *buffer_size, char *buffer)
+{
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI get_var_all(struct GblEfiFastbootProtocol *self,
+				       void *context, GetVarAllCallback cb)
+{
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI get_staged(struct GblEfiFastbootProtocol *self,
+				      size_t *buffer_size, size_t *buffer_remains,
+				      uint8_t *buffer)
+{
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t EFIAPI get_partition_type(struct GblEfiFastbootProtocol *self,
+					      const char *part_name,
+					      size_t *part_type_len, char *part_type)
+{
+	return EFI_UNSUPPORTED;
+}
+
+static efi_status_t handle_flash_bootloader(size_t download_buffer_used_size,
+					    uint8_t *download_buffer,
+					    FastbootMessageSender sender, void *context)
+{
+	if (sender) {
+		const char *msg = "Flashing bootloader via amlmmc...\n";
+		sender(context, GBL_EFI_FASTBOOT_MESSAGE_TYPE_INFO, strlen(msg), msg);
+	}
+
+	// Call amlmmc_write_bootloader
+	// We use AML_BL_BOOT to write to both boot0 and boot1
+	int ret = amlmmc_write_bootloader(CONFIG_FASTBOOT_FLASH_MMC_DEV,
+					  AML_BL_BOOT,
+					  download_buffer_used_size,
+					  download_buffer);
+	if (ret) {
+		if (sender) {
+			const char *msg = "Flash bootloader failed\n";
+			sender(context, GBL_EFI_FASTBOOT_MESSAGE_TYPE_FAIL, strlen(msg), msg);
+		}
+		return EFI_DEVICE_ERROR;
+	}
+
+	if (sender) {
+		const char *msg = "Flash bootloader success\n";
+		sender(context, GBL_EFI_FASTBOOT_MESSAGE_TYPE_OKAY, strlen(msg), msg);
+	}
+	return EFI_SUCCESS;
+}
+
+static efi_status_t handle_run_staged_efi(size_t download_buffer_used_size,
+					  uint8_t *download_buffer,
+					  FastbootMessageSender sender, void *context)
+{
+	if (download_buffer_used_size == 0 || !download_buffer) {
+		if (sender) {
+			const char *msg = "No EFI image staged\n";
+			sender(context, GBL_EFI_FASTBOOT_MESSAGE_TYPE_FAIL, strlen(msg), msg);
+		}
+		return EFI_NOT_FOUND;
+	}
+
+	if (sender) {
+		const char *msg = "Running staged EFI image...\n";
+		sender(context, GBL_EFI_FASTBOOT_MESSAGE_TYPE_INFO, strlen(msg), msg);
+	}
+
+	// We need to run the image.
+	// Similar to cb_run_staged_efi, we can use bootefi command.
+	char cmd[128];
+	snprintf(cmd, sizeof(cmd), "bootefi 0x%lx:0x%lx ${dtb_mem_addr}",
+		 (unsigned long)download_buffer,
+		 (unsigned long)download_buffer_used_size);
+
+	printf("Executing: %s\n", cmd);
+
+	// This might not return if successful
+	run_command(cmd, 0);
+
+	// If it returns, it failed or exited
+	if (sender) {
+		const char *msg = "EFI image returned\n";
+		sender(context, GBL_EFI_FASTBOOT_MESSAGE_TYPE_FAIL, strlen(msg), msg);
+	}
+	return EFI_DEVICE_ERROR;
+}
+
+typedef efi_status_t (*fastboot_cmd_handler_t)(size_t download_buffer_used_size,
+					      uint8_t *download_buffer,
+					      FastbootMessageSender sender, void *context);
+
+struct fastboot_cmd_match {
+	const char *const *args;
+	size_t num_args;
+	fastboot_cmd_handler_t handler;
+};
+
+#define CMD_ENTRIES_2(cb, w1, w2) \
+	{ \
+		.args = (const char*const[]){w1, w2}, \
+		.num_args = 2, \
+		.handler = cb, \
+	}, \
+	{ \
+		.args = (const char*const[]){w1 " " w2}, \
+		.num_args = 1, \
+		.handler = cb, \
+	}
+
+static const struct fastboot_cmd_match cmd_matches[] = {
+	CMD_ENTRIES_2(handle_flash_bootloader, "flash", "bootloader"),
+	CMD_ENTRIES_2(handle_run_staged_efi, "oem", "run-staged-efi"),
+};
+
+static efi_status_t EFIAPI command_exec(struct GblEfiFastbootProtocol *self,
+					size_t num_args, const char *const *args,
+					size_t download_buffer_size,
+					size_t download_buffer_used_size,
+					uint8_t *download_buffer,
+					GblEfiFastbootCommandExecResult *implementation,
+					FastbootMessageSender sender, void *context)
+{
+	EFI_ENTRY("%p, %zu, %p, %zu, %zu, %p", self, num_args, args,
+		  download_buffer_size, download_buffer_used_size, download_buffer);
+
+	if (num_args == 0 || !args || !implementation)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	// Default to not implemented by us
+	*implementation = GBL_EFI_FASTBOOT_COMMAND_EXEC_RESULT_DEFAULT_IMPL;
+
+	for (size_t i = 0; i < ARRAY_SIZE(cmd_matches); i++) {
+		const struct fastboot_cmd_match *match = &cmd_matches[i];
+		if (num_args < match->num_args)
+			continue;
+
+		bool found = true;
+		for (size_t j = 0; j < match->num_args; j++) {
+			if (strcmp(args[j], match->args[j]) != 0) {
+				found = false;
+				break;
+			}
+		}
+
+		if (found) {
+			*implementation = GBL_EFI_FASTBOOT_COMMAND_EXEC_RESULT_CUSTOM_IMPL;
+			return EFI_EXIT(match->handler(download_buffer_used_size,
+						      download_buffer,
+						      sender, context));
+		}
+	}
+
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const efi_guid_t guid = GBL_EFI_FASTBOOT_PROTOCOL_GUID;
+
+static GblEfiFastbootProtocol protocol = {
+	.revision = GBL_EFI_FASTBOOT_PROTOCOL_REVISION,
+	.serial_number = "VIM3-GBL-FASTBOOT",
+	.get_var = get_var,
+	.get_var_all = get_var_all,
+	.get_staged = get_staged,
+	.command_exec = command_exec,
+	.get_partition_type = get_partition_type,
+};
+
+efi_status_t efi_gbl_fastboot_register(void)
+{
+	char *serial = getenv("serial#");
+	if (serial) {
+		strncpy(protocol.serial_number, serial, sizeof(protocol.serial_number) - 1);
+		protocol.serial_number[sizeof(protocol.serial_number) - 1] = '\0';
+	}
+
+	efi_status_t ret = efi_add_protocol(efi_root, &guid, (void *)&protocol);
+	if (ret != EFI_SUCCESS)
+		printf("Cannot install GBL_EFI_FASTBOOT_PROTOCOL\n");
+	return ret;
+}
diff --git a/lib/efi_loader/gbl_efi_fastboot_transport.c b/lib/efi_loader/gbl_efi_fastboot_transport.c
index cba4dd5..266bd6c 100644
--- a/lib/efi_loader/gbl_efi_fastboot_transport.c
+++ b/lib/efi_loader/gbl_efi_fastboot_transport.c
@@ -1,5 +1,5 @@
 /*
- * Copyright (c) 2025 The Fuchsia Authors
+ * Copyright (c) 2026 The Fuchsia Authors
  *
  * SPDX-License-Identifier:	BSD-3-Clause
  */