blob: c32d06681a23e4e250bf738cde856ee4759da990 [file]
/*
* 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;
}