blob: e0d985707acbde2cdc488eaf497a94851cd978aa [file] [edit]
/*
* 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>
#include <version.h>
#include <g_dnl.h>
#include <fb_fastboot.h>
#include <partition_table.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)
{
EFI_ENTRY("%p, %zu, %p, %p, %p", self, num_args, args, buffer_size, buffer);
if (num_args == 0 || !args || !buffer_size || (*buffer_size > 0 && !buffer))
return EFI_EXIT(EFI_INVALID_PARAMETER);
char cmd_buf[RESPONSE_LEN];
char resp_buf[RESPONSE_LEN] = "";
snprintf(cmd_buf, sizeof(cmd_buf), "%s", args[0]);
if (fastboot_get_var(cmd_buf, resp_buf, sizeof(resp_buf) - 1) != 0)
return EFI_EXIT(EFI_NOT_FOUND);
size_t len = strlen(resp_buf);
if (*buffer_size < len + 1) {
*buffer_size = len + 1;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
strncpy(buffer, resp_buf, *buffer_size);
*buffer_size = len;
return EFI_EXIT(EFI_SUCCESS);
}
static efi_status_t EFIAPI get_var_all(struct GblEfiFastbootProtocol *self,
void *context, GetVarAllCallback cb)
{
EFI_ENTRY("%p, %p, %p", self, context, cb);
if (!cb)
return EFI_EXIT(EFI_INVALID_PARAMETER);
const char (*var_list)[RESPONSE_LEN];
size_t var_list_size;
if (has_boot_slot == 1) {
var_list = fastboot_getvar_list_ab_gbl_limited;
var_list_size = fastboot_getvar_list_ab_gbl_limited_size;
} else if (dynamic_partition) {
var_list = fastboot_getvar_list_dynamic;
var_list_size = fastboot_getvar_list_dynamic_size;
} else {
var_list = fastboot_getvar_list;
var_list_size = fastboot_getvar_list_size;
}
for (size_t i = 0; i < var_list_size; i++) {
const char *name = var_list[i];
char cmd_buf[RESPONSE_LEN];
char resp_buf[RESPONSE_LEN] = "";
snprintf(cmd_buf, sizeof(cmd_buf), "%s", name);
if (fastboot_get_var(cmd_buf, resp_buf, sizeof(resp_buf) - 1) == 0 &&
strncmp(resp_buf, "FAIL", 4) != 0) {
const char *const args[] = { name };
cb(context, 1, args, resp_buf);
}
}
return EFI_EXIT(EFI_SUCCESS);
}
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)
{
EFI_ENTRY("%p, %s, %p, %p", self, part_name, part_type_len, part_type);
if (!part_name || !part_type_len || (*part_type_len > 0 && !part_type))
return EFI_EXIT(EFI_INVALID_PARAMETER);
char cmd_buf[RESPONSE_LEN];
snprintf(cmd_buf, sizeof(cmd_buf), "partition-type:%s", part_name);
const char *part = parse_fb_cmd_partition(cmd_buf);
if (!part)
return EFI_EXIT(EFI_NOT_FOUND);
const char *type_str = "raw";
size_t len = strlen(type_str);
if (*part_type_len < len + 1) {
*part_type_len = len + 1;
return EFI_EXIT(EFI_BUFFER_TOO_SMALL);
}
strncpy(part_type, type_str, *part_type_len);
*part_type_len = len;
return EFI_EXIT(EFI_SUCCESS);
}
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;
}