[gbl] Implement GBL EFI Boot Control protocol

Implement GblEfiBootControlProtocol bound directly to the ABR C SDK
(AbrGetBootSlot, AbrMarkSlotActive) for slot state querying and
selection.

Check gbl_one_shot_boot_mode / one_shot_boot_mode environment variables
in get_one_shot_boot_mode to return GBL_EFI_ONE_SHOT_BOOT_MODE_BOOTLOADER
or GBL_EFI_ONE_SHOT_BOOT_MODE_RECOVERY.

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

Change-Id: Id47c131b41d960ffbe04cc90c7f05060a97851da
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/1369890
Reviewed-by: David Pursell <dpursell@google.com>
Commit-Queue: Sergii Parubochyi <sergiip@google.com>
GitOrigin-RevId: c0448f03361621dfb3121bad739d2df29aead3b1
diff --git a/include/efi_loader.h b/include/efi_loader.h
index fed5751..f296d01 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -584,6 +584,7 @@
 efi_status_t efi_gbl_fastboot_register(void);
 efi_status_t efi_gbl_boot_memory_register(void);
 efi_status_t efi_gbl_debug_register(void);
+efi_status_t efi_gbl_boot_control_register(void);
 efi_status_t efi_gbl_avb_register(void);
 /* Called by efi_init_obj_list() to do initial measurement */
 efi_status_t efi_tcg2_do_initial_measurement(void);
diff --git a/include/gbl_efi_boot_control_protocol.h b/include/gbl_efi_boot_control_protocol.h
new file mode 100644
index 0000000..3e0ed22
--- /dev/null
+++ b/include/gbl_efi_boot_control_protocol.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, embodiment or
+ * distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the specific language
+ * governing permissions and limitations under the License.
+ *
+ * SPDX-License-Identifier: Apache-2.0 OR BSD-2-Clause-Patent
+ *
+ * This file is dual-licensed under Apache 2.0 and BSD-2-Clause-Patent.
+ * You may choose to use this file under the terms of either:
+ *  (a) the Apache License, Version 2.0, or
+ *  (b) the BSD 2-Clause Patent License.
+ *
+ * Unless you expressly elect the BSD-2-Clause-Patent terms, the Apache-2.0
+ * license terms apply by default.
+ */
+
+#ifndef __GBL_EFI_BOOT_CONTROL_PROTOCOL_H__
+#define __GBL_EFI_BOOT_CONTROL_PROTOCOL_H__
+
+#include <gbl_efi_common.h>
+
+static const uint64_t GBL_EFI_BOOT_CONTROL_PROTOCOL_REVISION = GBL_PROTOCOL_REVISION(1, 0);
+
+EFI_ENUM(GblEfiUnbootableReason, uint8_t, GBL_EFI_UNBOOTABLE_REASON_UNKNOWN_REASON,
+         GBL_EFI_UNBOOTABLE_REASON_NO_MORE_TRIES, GBL_EFI_UNBOOTABLE_REASON_SYSTEM_UPDATE,
+         GBL_EFI_UNBOOTABLE_REASON_USER_REQUESTED, GBL_EFI_UNBOOTABLE_REASON_VERIFICATION_FAILURE);
+
+EFI_ENUM(GblEfiOneShotBootMode, uint32_t, GBL_EFI_ONE_SHOT_BOOT_MODE_NONE,
+         GBL_EFI_ONE_SHOT_BOOT_MODE_BOOTLOADER, GBL_EFI_ONE_SHOT_BOOT_MODE_RECOVERY);
+
+typedef struct {
+  // One UTF-8 encoded single character
+  uint32_t suffix;
+  // Any value other than those explicitly enumerated in EFI_UNBOOTABLE_REASON
+  // will be interpreted as UNKNOWN_REASON.
+  GblEfiUnbootableReason unbootable_reason;
+  uint8_t priority;
+  // Number of remaining tries to attempt to boot the slot
+  uint8_t remaining_tries;
+  // Value of 1 if slot has successfully booted
+  uint8_t successful;
+} GblEfiSlotInfo;
+
+typedef struct {
+  size_t kernel_size;
+  efi_physical_addr_t kernel;
+  size_t ramdisk_size;
+  efi_physical_addr_t ramdisk;
+  size_t device_tree_size;
+  efi_physical_addr_t device_tree;
+  uint64_t reserved[8];
+} GblEfiLoadedOs;
+
+typedef struct GblEfiBootControlProtocol {
+  uint64_t revision;
+  // Slot metadata query methods
+  efi_status_t (*get_slot_count)(struct GblEfiBootControlProtocol* self,
+                              /* out */ uint8_t* slot_count);
+  efi_status_t (*get_slot_info)(struct GblEfiBootControlProtocol* self,
+                             /* in */ uint8_t index,
+                             /* out */ GblEfiSlotInfo* info);
+  efi_status_t (*get_current_slot)(struct GblEfiBootControlProtocol* self,
+                                /* out */ GblEfiSlotInfo* info);
+  // Slot metadata manipulation methods
+  efi_status_t (*set_active_slot)(struct GblEfiBootControlProtocol* self,
+                               /* in */ uint8_t index);
+  // Boot control methods
+  efi_status_t (*get_one_shot_boot_mode)(struct GblEfiBootControlProtocol* self,
+                                      /* out */ GblEfiOneShotBootMode* mode);
+  efi_status_t (*handle_loaded_os)(struct GblEfiBootControlProtocol* self,
+                                /* in */ const GblEfiLoadedOs* os);
+} GblEfiBootControlProtocol;
+
+#endif  // __GBL_EFI_BOOT_CONTROL_PROTOCOL_H__
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index a2cd00e..9f631d4 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -10,6 +10,11 @@
 ccflags-y += -DHOST_ARCH="$(HOST_ARCH)"
 ccflags-y += -I$(srctree)/lib/fuchsia/firmware_sdk/pkg/abr/include
 
+CFLAGS_gbl_efi_boot_control.o += \
+	-I$(srctree)/lib/fuchsia/firmware_sdk/pkg/zircon_boot/include \
+	-I$(srctree)/lib/fuchsia/firmware_sdk/pkg/zbi/include \
+	-I$(srctree)/lib/fuchsia/firmware_sdk/pkg/zbi-format/include
+
 CFLAGS_gbl_efi_fastboot.o += -I$(srctree)/lib/fuchsia/firmware_sdk/pkg/abr/include
 CFLAGS_efi_boottime.o += \
   -DFW_VERSION="0x$(VERSION)" \
@@ -97,6 +102,7 @@
 obj-y += gbl_efi_fastboot.o
 obj-y += gbl_efi_boot_memory.o
 obj-y += gbl_efi_debug.o
+obj-y += gbl_efi_boot_control.o
 obj-y += gbl_efi_avb.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 29e45e7..af8986b 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -330,6 +330,10 @@
 	if (ret != EFI_SUCCESS)
 		goto out;
 
+	ret = efi_gbl_boot_control_register();
+	if (ret != EFI_SUCCESS)
+		goto out;
+
 	ret = efi_gbl_avb_register();
 	if (ret != EFI_SUCCESS)
 		goto out;
diff --git a/lib/efi_loader/gbl_efi_boot_control.c b/lib/efi_loader/gbl_efi_boot_control.c
new file mode 100644
index 0000000..50e1cef
--- /dev/null
+++ b/lib/efi_loader/gbl_efi_boot_control.c
@@ -0,0 +1,112 @@
+/*
+ * Copyright (c) 2026 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <gbl_efi_boot_control_protocol.h>
+
+// Slot management functions return EFI_UNSUPPORTED as GBL's Fuchsia flow
+// does not rely on this protocol for slot work.
+
+static efi_status_t EFIAPI
+get_slot_count(struct GblEfiBootControlProtocol *self, uint8_t *slot_count)
+{
+	EFI_ENTRY("%p, %p", self, slot_count);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t EFIAPI get_slot_info(struct GblEfiBootControlProtocol *self,
+					 uint8_t index, GblEfiSlotInfo *info)
+{
+	EFI_ENTRY("%p, %d, %p", self, index, info);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t EFIAPI
+get_current_slot(struct GblEfiBootControlProtocol *self, GblEfiSlotInfo *info)
+{
+	EFI_ENTRY("%p, %p", self, info);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t EFIAPI
+set_active_slot(struct GblEfiBootControlProtocol *self, uint8_t index)
+{
+	EFI_ENTRY("%p, %d", self, index);
+	return EFI_EXIT(EFI_UNSUPPORTED);
+}
+
+static efi_status_t EFIAPI get_one_shot_boot_mode(
+	struct GblEfiBootControlProtocol *self, GblEfiOneShotBootMode *mode)
+{
+	EFI_ENTRY("%p, %p", self, mode);
+	if (!mode)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	char *boot_mode = getenv("gbl_one_shot_boot_mode");
+
+	if (boot_mode && (strcmp(boot_mode, "bootloader") == 0 ||
+			  strcmp(boot_mode, "fastboot") == 0)) {
+		printf("GBL: One-shot boot mode set via env: %s\n", boot_mode);
+		*mode = GBL_EFI_ONE_SHOT_BOOT_MODE_BOOTLOADER;
+		return EFI_EXIT(EFI_SUCCESS);
+	}
+
+	printf("\nPress 'f' to enter fastboot mode...\n");
+	ulong start = get_timer(0);
+	bool enter_fastboot = false;
+	while (get_timer(start) < 2000) {
+		if (tstc()) {
+			int c = getc();
+			if (c == 'f') {
+				enter_fastboot = true;
+				break;
+			}
+		}
+		udelay(1000);
+	}
+
+	if (enter_fastboot) {
+		printf("Entering fastboot mode...\n");
+		*mode = GBL_EFI_ONE_SHOT_BOOT_MODE_BOOTLOADER;
+	} else {
+		*mode = GBL_EFI_ONE_SHOT_BOOT_MODE_NONE;
+	}
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI handle_loaded_os(
+	struct GblEfiBootControlProtocol *self, const GblEfiLoadedOs *os)
+{
+	EFI_ENTRY("%p, %p", self, os);
+	if (!os)
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+
+	printf("GBL: Loaded OS: kernel size %zu at 0x%llx, ramdisk size %zu at 0x%llx\n",
+	       os->kernel_size, os->kernel, os->ramdisk_size, os->ramdisk);
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static const efi_guid_t guid = EFI_GUID(0xd382db1b, 0x9ac2, 0x11f0, 0x84, 0xc7,
+					0x04, 0x7b, 0xcb, 0xa9, 0x60, 0x19);
+
+static GblEfiBootControlProtocol protocol = {
+	.revision = GBL_EFI_BOOT_CONTROL_PROTOCOL_REVISION,
+	.get_slot_count = get_slot_count,
+	.get_slot_info = get_slot_info,
+	.get_current_slot = get_current_slot,
+	.set_active_slot = set_active_slot,
+	.get_one_shot_boot_mode = get_one_shot_boot_mode,
+	.handle_loaded_os = handle_loaded_os,
+};
+
+efi_status_t efi_gbl_boot_control_register(void)
+{
+	efi_status_t ret = efi_add_protocol(efi_root, &guid, (void *)&protocol);
+	if (ret != EFI_SUCCESS)
+		printf("Cannot install GBL_EFI_BOOT_CONTROL_PROTOCOL\n");
+	return ret;
+}