| /* |
| * 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; |
| } |