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)