ANDROID: [efi] Implement EFI_ANDROID_BOOT_PROTOCOL

Bug: 332898242
Change-Id: I697b6a41931b3fe2e83ba668f0a25a9cfa745753
Reviewed-on: https://turquoise-internal-review.googlesource.com/c/third_party/u-boot/+/827730
Reviewed-by: David Pursell <dpursell@google.com>
GitOrigin-RevId: bb768abe02c3e662ce529489708eb0d4e396f8ac

Kanged from https://third-party-mirror.googlesource.com/u-boot/+/798887e3c96f3962367b17d45023e7147d3d8195
Change-Id: I3a8e6d2566985e2d0b4bc2459a15fc051da30aab
Signed-off-by: Amit Pundir <amit.pundir@linaro.org>
diff --git a/drivers/usb/gadget/f_fastboot.c b/drivers/usb/gadget/f_fastboot.c
index 7fca24a..61c287a 100644
--- a/drivers/usb/gadget/f_fastboot.c
+++ b/drivers/usb/gadget/f_fastboot.c
@@ -24,6 +24,8 @@
 #include <linux/compiler.h>
 #include <g_dnl.h>
 
+#include <fastboot_usb.h>
+
 #define FASTBOOT_INTERFACE_CLASS	0xff
 #define FASTBOOT_INTERFACE_SUB_CLASS	0x42
 #define FASTBOOT_INTERFACE_PROTOCOL	0x03
@@ -39,14 +41,6 @@
  * that expect bulk OUT requests to be divisible by maxpacket size.
  */
 
-struct f_fastboot {
-	struct usb_function usb_function;
-
-	/* IN/OUT EP's and corresponding requests */
-	struct usb_ep *in_ep, *out_ep;
-	struct usb_request *in_req, *out_req;
-};
-
 static char fb_ext_prop_name[] = "DeviceInterfaceGUID";
 static char fb_ext_prop_data[] = "{4866319A-F4D6-4374-93B9-DC2DEB361BA9}";
 
@@ -71,7 +65,27 @@
 	return container_of(f, struct f_fastboot, usb_function);
 }
 
-static struct f_fastboot *fastboot_func;
+static void fastboot_complete(struct usb_ep *ep, struct usb_request *req);
+static void rx_handler_command(struct usb_ep *ep, struct usb_request *req);
+
+struct f_fastboot *fastboot_func;
+static usb_req_cb in_req_completion_cb = fastboot_complete;
+static usb_req_cb out_req_completion_cb = rx_handler_command;
+
+int set_fastboot_usb_completion_cb(usb_req_cb out_req, usb_req_cb in_req) {
+	// Disallow if Fastboot function is in operation.
+	if (fastboot_func && fastboot_func->in_ep && fastboot_func->out_ep) {
+		return -1;
+	}
+	out_req_completion_cb = out_req;
+	in_req_completion_cb = in_req;
+	return 0;
+}
+
+void reset_fastboot_usb_completion_cb(void) {
+	in_req_completion_cb = fastboot_complete;
+	out_req_completion_cb = rx_handler_command;
+}
 
 static struct usb_endpoint_descriptor fs_ep_in = {
 	.bLength            = USB_DT_ENDPOINT_SIZE,
@@ -339,7 +353,7 @@
 		ret = -EINVAL;
 		goto err;
 	}
-	f_fb->out_req->complete = rx_handler_command;
+	f_fb->out_req->complete = out_req_completion_cb;
 
 	d = fb_ep_desc(gadget, &fs_ep_in, &hs_ep_in, &ss_ep_in);
 	ret = usb_ep_enable(f_fb->in_ep, d);
@@ -354,7 +368,7 @@
 		ret = -EINVAL;
 		goto err;
 	}
-	f_fb->in_req->complete = fastboot_complete;
+	f_fb->in_req->complete = in_req_completion_cb;
 
 	ret = usb_ep_queue(f_fb->out_ep, f_fb->out_req, 0);
 	if (ret)
diff --git a/include/efi_android_boot.h b/include/efi_android_boot.h
new file mode 100644
index 0000000..4fc1859
--- /dev/null
+++ b/include/efi_android_boot.h
@@ -0,0 +1,34 @@
+/*
+ * Copyright (c) 2024 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+// EFI_ANDROID_BOOT_PROTOCOL is a UEFI protocol introduced by Google Generic
+// Bootloader for verified booting and Fastboot over USB.
+// See https://android.googlesource.com/platform/bootable/libbootloader/+/refs/heads/main/gbl/docs/EFI_ANDROID_BOOT_PROTOCOL.md
+// for protocol spec.
+
+#include <efi_api.h>
+
+#define EFI_ANDROID_BOOT_PROTOCOL_REVISION 0x00000000
+
+#define EFI_ANDROID_BOOT_PROTOCOL_GUID                                     \
+	EFI_GUID(0x6281a893, 0xac23, 0x4ca7, 0xb2, 0x81, 0x34, 0x0e, 0xf8, \
+		 0x16, 0x89, 0x55)
+
+typedef struct efi_android_boot_protocol {
+	uint64_t revision;
+	efi_status_t (*fastboot_usb_interface_start)(
+		struct efi_android_boot_protocol *self,
+		size_t *max_packet_size);
+	efi_status_t (*fastboot_usb_interface_stop)(
+		struct efi_android_boot_protocol *self);
+	efi_status_t (*fastboot_usb_receive)(
+		struct efi_android_boot_protocol *self, size_t *buffer_size,
+		void *buffer);
+	efi_status_t (*fastboot_usb_send)(struct efi_android_boot_protocol *self,
+					  size_t *buffer_size,
+					  const void *buffer);
+	struct efi_event *wait_for_send_completion;
+} efi_android_boot_protocol;
diff --git a/include/efi_loader.h b/include/efi_loader.h
index e244105..294f0c3 100644
--- a/include/efi_loader.h
+++ b/include/efi_loader.h
@@ -568,6 +568,8 @@
 efi_status_t efi_tcg2_register(void);
 /* Called by efi_init_obj_list() to install RISCV_EFI_BOOT_PROTOCOL */
 efi_status_t efi_riscv_register(void);
+/* Called by efi_init_obj_list() to install EFI_ANDROID_BOOT_PROTOCOL */
+efi_status_t efi_android_boot_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/fastboot_usb.h b/include/fastboot_usb.h
new file mode 100644
index 0000000..2c42ee7
--- /dev/null
+++ b/include/fastboot_usb.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2024 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include <linux/usb/gadget.h>
+#include <linux/usb/composite.h>
+
+typedef void (*usb_req_cb)(struct usb_ep *ep, struct usb_request *req);
+
+struct f_fastboot {
+	struct usb_function usb_function;
+
+	/* IN/OUT EP's and corresponding requests */
+	struct usb_ep *in_ep, *out_ep;
+	struct usb_request *in_req, *out_req;
+};
+
+// `fastboot_func` contains USB IN/OUT EP's and the corresponding USB requests.
+extern struct f_fastboot *fastboot_func;
+
+// Sets custom Fastboot USB completion callback.
+int set_fastboot_usb_completion_cb(usb_req_cb out_req, usb_req_cb in_req);
+
+// Resets to default Fastboot USB completion callback.
+void reset_fastboot_usb_completion_cb(void);
diff --git a/lib/efi_loader/Makefile b/lib/efi_loader/Makefile
index 8d31fc6..bd353eb 100644
--- a/lib/efi_loader/Makefile
+++ b/lib/efi_loader/Makefile
@@ -87,5 +87,7 @@
 obj-$(CONFIG_EFI_SIGNATURE_SUPPORT) += efi_signature.o
 obj-$(CONFIG_EFI_ECPT) += efi_conformance.o
 
+obj-y += efi_android_boot.o
+
 EFI_VAR_SEED_FILE := $(subst $\",,$(CONFIG_EFI_VAR_SEED_FILE))
 $(obj)/efi_var_seed.o: $(srctree)/$(EFI_VAR_SEED_FILE)
diff --git a/lib/efi_loader/efi_android_boot.c b/lib/efi_loader/efi_android_boot.c
new file mode 100644
index 0000000..5ef7fb6
--- /dev/null
+++ b/lib/efi_loader/efi_android_boot.c
@@ -0,0 +1,232 @@
+/*
+ * Copyright (c) 2024 The Fuchsia Authors
+ *
+ * SPDX-License-Identifier:	BSD-3-Clause
+ */
+
+#include <common.h>
+#include <efi_loader.h>
+#include <log.h>
+#include <asm/global_data.h>
+#include <efi_android_boot.h>
+#include <g_dnl.h>
+#include <fastboot_usb.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+static const efi_guid_t efi_guid_android_boot_protocol =
+	EFI_ANDROID_BOOT_PROTOCOL_GUID;
+
+static struct android_boot_protocol_instance {
+	efi_android_boot_protocol protocol;
+	struct efi_event *timer_event;
+	bool send_completion_already_signaled;
+} android_boot;
+
+// USB driver requires a mandatory callback. We simply use a noop placeholder.
+// The implementation polls send/receive status directly.
+static void noop_cb(struct usb_ep *, struct usb_request *)
+{
+}
+
+// Checks whether Fastboot USB endpoints are allocated.
+static bool is_started(void)
+{
+	return fastboot_func && fastboot_func->in_ep && fastboot_func->out_ep;
+}
+
+static efi_status_t EFIAPI fastboot_usb_interface_start(
+	struct efi_android_boot_protocol *self, size_t *max_packet_size)
+{
+	EFI_ENTRY("%p, %p", self, max_packet_size);
+
+	if (is_started()) {
+		return EFI_EXIT(EFI_ALREADY_STARTED);
+	} else if (!max_packet_size) {
+		return EFI_EXIT(EFI_INVALID_PARAMETER);
+	}
+
+	int ret = set_fastboot_usb_completion_cb(noop_cb, noop_cb);
+	if (ret) {
+		printf("usb_dnl_fastboot failed to set cb: %d\n", ret);
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+
+	ret = g_dnl_register("usb_dnl_fastboot");
+	if (ret) {
+		printf("usb_dnl_fastboot register failed: %d\n", ret);
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+
+	if (!is_started()) {
+		printf("Fastboot end points not allocated\n");
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+
+	*max_packet_size = fastboot_func->in_ep->maxpacket;
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+fastboot_usb_interface_stop(struct efi_android_boot_protocol *self)
+{
+	EFI_ENTRY("%p", self);
+
+	if (!is_started()) {
+		return EFI_EXIT(EFI_NOT_STARTED);
+	}
+
+	g_dnl_unregister();
+	run_command("usb reset", 0);
+	reset_fastboot_usb_completion_cb();
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+fastboot_usb_receive(struct efi_android_boot_protocol *self,
+		     size_t *buffer_size, void *buffer)
+{
+	EFI_ENTRY("%p, %p", buffer_size, buffer);
+
+	if (!is_started()) {
+		return EFI_EXIT(EFI_NOT_STARTED);
+	}
+
+	// Executes any event check and notification payload. This includes our
+	// `efi_fastboot_usb_timer_notify` notify function.
+	efi_timer_check();
+
+	if (!fastboot_func->out_req ||
+	    fastboot_func->out_req->status == -EINPROGRESS) {
+		return EFI_EXIT(EFI_NOT_READY);
+	} else if (fastboot_func->out_req->status != 0) {
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	} else if (*buffer_size < (size_t)fastboot_func->out_req->actual) {
+		*buffer_size = (size_t)fastboot_func->out_req->actual;
+		return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
+	}
+
+	memcpy(buffer, fastboot_func->out_req->buf,
+	       (size_t)fastboot_func->out_req->actual);
+	*buffer_size = (size_t)fastboot_func->out_req->actual;
+	// Queues back the USB rx request to receive the next packet.
+	if (usb_ep_queue(fastboot_func->out_ep, fastboot_func->out_req, 0)) {
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static efi_status_t EFIAPI
+fastboot_usb_send(struct efi_android_boot_protocol *self, size_t *buffer_size,
+		  const void *buffer)
+{
+	EFI_ENTRY("%p, %p", buffer_size, buffer);
+
+	if (!is_started()) {
+		return EFI_EXIT(EFI_NOT_STARTED);
+	}
+
+	// Executes any event check and notification payload. This includes our
+	// `efi_fastboot_usb_timer_notify` notify function.
+	efi_timer_check();
+
+	if (!fastboot_func->in_req &&
+	    fastboot_func->in_req->status == -EINPROGRESS) {
+		return EFI_EXIT(EFI_NOT_READY);
+	} else if (fastboot_func->in_req->status != 0) {
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	} else if (*buffer_size > (size_t)fastboot_func->in_ep->maxpacket) {
+		*buffer_size = (size_t)fastboot_func->in_ep->maxpacket;
+		return EFI_EXIT(EFI_BAD_BUFFER_SIZE);
+	}
+
+	// Resets event signal state.
+	android_boot.send_completion_already_signaled = false;
+	self->wait_for_send_completion->is_signaled = false;
+	memcpy(fastboot_func->in_req->buf, buffer, *buffer_size);
+	fastboot_func->in_req->length = *buffer_size;
+	if (usb_ep_queue(fastboot_func->in_ep, fastboot_func->in_req, 0)) {
+		return EFI_EXIT(EFI_DEVICE_ERROR);
+	}
+	return EFI_EXIT(EFI_SUCCESS);
+}
+
+static struct android_boot_protocol_instance android_boot = {
+    .protocol = {
+        .revision = EFI_ANDROID_BOOT_PROTOCOL_REVISION,
+        .fastboot_usb_interface_start = fastboot_usb_interface_start,
+        .fastboot_usb_interface_stop = fastboot_usb_interface_stop,
+        .fastboot_usb_receive = fastboot_usb_receive,
+        .fastboot_usb_send = fastboot_usb_send,
+        .wait_for_send_completion = NULL,
+    },
+    .timer_event = NULL,
+    .send_completion_already_signaled = false,
+};
+
+// A timer event notification function that polls send/receive status and
+// signals event.
+static void EFIAPI efi_fastboot_usb_timer_notify(struct efi_event *event,
+						 void *context)
+{
+	EFI_ENTRY("%p, %p", event, context);
+	efi_android_boot_protocol *protocol = &android_boot.protocol;
+	if (!is_started() || !protocol->wait_for_send_completion) {
+		EFI_EXIT(EFI_SUCCESS);
+		return;
+	}
+
+	usb_gadget_handle_interrupts();
+	if (fastboot_func->in_req->status == 0 &&
+	    !android_boot.send_completion_already_signaled) {
+		android_boot.send_completion_already_signaled = true;
+		protocol->wait_for_send_completion->is_signaled = true;
+	}
+	EFI_EXIT(EFI_SUCCESS);
+}
+
+static void EFIAPI efi_fastboot_usb_wait_notify(struct efi_event *, void *)
+{
+	// Nothing to do for `wait_for_send_completion` notificiaiton. But UEFI
+	// still requires it.
+}
+
+// Registers EFI_ANDROID_BOOT_PROTOCOL
+efi_status_t efi_android_boot_register(void)
+{
+	efi_status_t ret = efi_add_protocol(efi_root,
+					    &efi_guid_android_boot_protocol,
+					    (void *)&android_boot.protocol);
+	if (ret != EFI_SUCCESS)
+		printf("Cannot install EFI_ANDROID_BOOT_PROTOCOL\n");
+
+	// Create a `wait_for_send_completion` event.
+	ret = efi_create_event(EVT_NOTIFY_WAIT, TPL_CALLBACK,
+			       efi_fastboot_usb_wait_notify, NULL, NULL,
+			       &android_boot.protocol.wait_for_send_completion);
+	if (ret != EFI_SUCCESS) {
+		printf("Failed to create wait_for_send_completion event\n");
+		return ret;
+	}
+
+	// Create a timer event so that the notify function can get triggered
+	// whenever the EFI boot service processes any event.
+	ret = efi_create_event(EVT_TIMER | EVT_NOTIFY_SIGNAL, TPL_NOTIFY,
+			       efi_fastboot_usb_timer_notify, NULL, NULL,
+			       &android_boot.timer_event);
+	if (ret != EFI_SUCCESS) {
+		printf("Failed to add EFI_ANDROID_BOOT_PROTOCOL timer event\n");
+		return ret;
+	}
+	// Fastboot USB is time critical, Triggers the event in every timer cycle
+	// Set timer period to 0. This is equivalent to invoking
+	// `efi_fastboot_usb_timer_notify` every time boot service check any event
+	// i.e. in `efi_timer_check()`
+	ret = efi_set_timer(android_boot.timer_event, EFI_TIMER_PERIODIC, 0);
+	if (ret != EFI_SUCCESS) {
+		printf("Failed to set EFI_ANDROID_BOOT_PROTOCOL timer\n");
+		return ret;
+	}
+
+	return EFI_SUCCESS;
+}
diff --git a/lib/efi_loader/efi_setup.c b/lib/efi_loader/efi_setup.c
index e6de685..2fe25a4 100644
--- a/lib/efi_loader/efi_setup.c
+++ b/lib/efi_loader/efi_setup.c
@@ -295,6 +295,10 @@
 			goto out;
 	}
 
+	ret = efi_android_boot_register();
+	if (ret != EFI_SUCCESS)
+		goto out;
+
 	/* Secure boot */
 	ret = efi_init_secure_boot();
 	if (ret != EFI_SUCCESS)