Merge UDP fastboot support from AOSP: https://android.googlesource.com/platform/external/u-boot/+/android-o-mr1-iot-preview-8 Signed-off-by: Alex Kiernan <alex.kiernan@gmail.com> Signed-off-by: Alex Deymo <deymo@google.com> Signed-off-by: Jocelyn Bohr <bohr@google.com> Reviewed-by: Simon Glass <sjg@chromium.org>lime2-spi
parent
c232d14d11
commit
f73a7df984
@ -1,6 +1,7 @@ |
|||||||
# SPDX-License-Identifier: GPL-2.0+
|
# SPDX-License-Identifier: GPL-2.0+
|
||||||
|
|
||||||
obj-y += fb_common.o
|
obj-y += fb_common.o
|
||||||
|
obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_getvar.o
|
||||||
|
obj-$(CONFIG_UDP_FUNCTION_FASTBOOT) += fb_command.o
|
||||||
obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o
|
obj-$(CONFIG_FASTBOOT_FLASH_MMC) += fb_mmc.o
|
||||||
obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
|
obj-$(CONFIG_FASTBOOT_FLASH_NAND) += fb_nand.o
|
||||||
|
@ -0,0 +1,302 @@ |
|||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <fastboot.h> |
||||||
|
#include <fastboot-internal.h> |
||||||
|
#include <fb_mmc.h> |
||||||
|
#include <fb_nand.h> |
||||||
|
#include <part.h> |
||||||
|
#include <stdlib.h> |
||||||
|
|
||||||
|
/**
|
||||||
|
* image_size - final fastboot image size |
||||||
|
*/ |
||||||
|
static u32 image_size; |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_bytes_received - number of bytes received in the current download |
||||||
|
*/ |
||||||
|
static u32 fastboot_bytes_received; |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_bytes_expected - number of bytes expected in the current download |
||||||
|
*/ |
||||||
|
static u32 fastboot_bytes_expected; |
||||||
|
|
||||||
|
static void okay(char *, char *); |
||||||
|
static void getvar(char *, char *); |
||||||
|
static void download(char *, char *); |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
static void flash(char *, char *); |
||||||
|
static void erase(char *, char *); |
||||||
|
#endif |
||||||
|
static void reboot_bootloader(char *, char *); |
||||||
|
|
||||||
|
static const struct { |
||||||
|
const char *command; |
||||||
|
void (*dispatch)(char *cmd_parameter, char *response); |
||||||
|
} commands[FASTBOOT_COMMAND_COUNT] = { |
||||||
|
[FASTBOOT_COMMAND_GETVAR] = { |
||||||
|
.command = "getvar", |
||||||
|
.dispatch = getvar |
||||||
|
}, |
||||||
|
[FASTBOOT_COMMAND_DOWNLOAD] = { |
||||||
|
.command = "download", |
||||||
|
.dispatch = download |
||||||
|
}, |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
[FASTBOOT_COMMAND_FLASH] = { |
||||||
|
.command = "flash", |
||||||
|
.dispatch = flash |
||||||
|
}, |
||||||
|
[FASTBOOT_COMMAND_ERASE] = { |
||||||
|
.command = "erase", |
||||||
|
.dispatch = erase |
||||||
|
}, |
||||||
|
#endif |
||||||
|
[FASTBOOT_COMMAND_BOOT] = { |
||||||
|
.command = "boot", |
||||||
|
.dispatch = okay |
||||||
|
}, |
||||||
|
[FASTBOOT_COMMAND_CONTINUE] = { |
||||||
|
.command = "continue", |
||||||
|
.dispatch = okay |
||||||
|
}, |
||||||
|
[FASTBOOT_COMMAND_REBOOT] = { |
||||||
|
.command = "reboot", |
||||||
|
.dispatch = okay |
||||||
|
}, |
||||||
|
[FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = { |
||||||
|
.command = "reboot-bootloader", |
||||||
|
.dispatch = reboot_bootloader |
||||||
|
}, |
||||||
|
[FASTBOOT_COMMAND_SET_ACTIVE] = { |
||||||
|
.command = "set_active", |
||||||
|
.dispatch = okay |
||||||
|
}, |
||||||
|
}; |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_handle_command - Handle fastboot command |
||||||
|
* |
||||||
|
* @cmd_string: Pointer to command string |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Return: Executed command, or -1 if not recognized |
||||||
|
*/ |
||||||
|
int fastboot_handle_command(char *cmd_string, char *response) |
||||||
|
{ |
||||||
|
int i; |
||||||
|
char *cmd_parameter; |
||||||
|
|
||||||
|
cmd_parameter = cmd_string; |
||||||
|
strsep(&cmd_parameter, ":"); |
||||||
|
|
||||||
|
for (i = 0; i < FASTBOOT_COMMAND_COUNT; i++) { |
||||||
|
if (!strcmp(commands[i].command, cmd_string)) { |
||||||
|
if (commands[i].dispatch) { |
||||||
|
commands[i].dispatch(cmd_parameter, |
||||||
|
response); |
||||||
|
return i; |
||||||
|
} else { |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pr_err("command %s not recognized.\n", cmd_string); |
||||||
|
fastboot_fail("unrecognized command", response); |
||||||
|
return -1; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* okay() - Send bare OKAY response |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to command parameter |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Send a bare OKAY fastboot response. This is used where the command is |
||||||
|
* valid, but all the work is done after the response has been sent (e.g. |
||||||
|
* boot, reboot etc.) |
||||||
|
*/ |
||||||
|
static void okay(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_okay(NULL, response); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* getvar() - Read a config/version variable |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to command parameter |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
*/ |
||||||
|
static void getvar(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_getvar(cmd_parameter, response); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_download() - Start a download transfer from the client |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to command parameter |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
*/ |
||||||
|
static void download(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
char *tmp; |
||||||
|
|
||||||
|
if (!cmd_parameter) { |
||||||
|
fastboot_fail("Expected command parameter", response); |
||||||
|
return; |
||||||
|
} |
||||||
|
fastboot_bytes_received = 0; |
||||||
|
fastboot_bytes_expected = simple_strtoul(cmd_parameter, &tmp, 16); |
||||||
|
if (fastboot_bytes_expected == 0) { |
||||||
|
fastboot_fail("Expected nonzero image size", response); |
||||||
|
return; |
||||||
|
} |
||||||
|
/*
|
||||||
|
* Nothing to download yet. Response is of the form: |
||||||
|
* [DATA|FAIL]$cmd_parameter |
||||||
|
* |
||||||
|
* where cmd_parameter is an 8 digit hexadecimal number |
||||||
|
*/ |
||||||
|
if (fastboot_bytes_expected > fastboot_buf_size) { |
||||||
|
fastboot_fail(cmd_parameter, response); |
||||||
|
} else { |
||||||
|
printf("Starting download of %d bytes\n", |
||||||
|
fastboot_bytes_expected); |
||||||
|
fastboot_response("DATA", response, "%s", cmd_parameter); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_data_remaining() - return bytes remaining in current transfer |
||||||
|
* |
||||||
|
* Return: Number of bytes left in the current download |
||||||
|
*/ |
||||||
|
u32 fastboot_data_remaining(void) |
||||||
|
{ |
||||||
|
return fastboot_bytes_expected - fastboot_bytes_received; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_data_download() - Copy image data to fastboot_buf_addr. |
||||||
|
* |
||||||
|
* @fastboot_data: Pointer to received fastboot data |
||||||
|
* @fastboot_data_len: Length of received fastboot data |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Copies image data from fastboot_data to fastboot_buf_addr. Writes to |
||||||
|
* response. fastboot_bytes_received is updated to indicate the number |
||||||
|
* of bytes that have been transferred. |
||||||
|
* |
||||||
|
* On completion sets image_size and ${filesize} to the total size of the |
||||||
|
* downloaded image. |
||||||
|
*/ |
||||||
|
void fastboot_data_download(const void *fastboot_data, |
||||||
|
unsigned int fastboot_data_len, |
||||||
|
char *response) |
||||||
|
{ |
||||||
|
#define BYTES_PER_DOT 0x20000 |
||||||
|
u32 pre_dot_num, now_dot_num; |
||||||
|
|
||||||
|
if (fastboot_data_len == 0 || |
||||||
|
(fastboot_bytes_received + fastboot_data_len) > |
||||||
|
fastboot_bytes_expected) { |
||||||
|
fastboot_fail("Received invalid data length", |
||||||
|
response); |
||||||
|
return; |
||||||
|
} |
||||||
|
/* Download data to fastboot_buf_addr */ |
||||||
|
memcpy(fastboot_buf_addr + fastboot_bytes_received, |
||||||
|
fastboot_data, fastboot_data_len); |
||||||
|
|
||||||
|
pre_dot_num = fastboot_bytes_received / BYTES_PER_DOT; |
||||||
|
fastboot_bytes_received += fastboot_data_len; |
||||||
|
now_dot_num = fastboot_bytes_received / BYTES_PER_DOT; |
||||||
|
|
||||||
|
if (pre_dot_num != now_dot_num) { |
||||||
|
putc('.'); |
||||||
|
if (!(now_dot_num % 74)) |
||||||
|
putc('\n'); |
||||||
|
} |
||||||
|
*response = '\0'; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_data_complete() - Mark current transfer complete |
||||||
|
* |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Set image_size and ${filesize} to the total size of the downloaded image. |
||||||
|
*/ |
||||||
|
void fastboot_data_complete(char *response) |
||||||
|
{ |
||||||
|
/* Download complete. Respond with "OKAY" */ |
||||||
|
fastboot_okay(NULL, response); |
||||||
|
printf("\ndownloading of %d bytes finished\n", fastboot_bytes_received); |
||||||
|
image_size = fastboot_bytes_received; |
||||||
|
env_set_hex("filesize", image_size); |
||||||
|
fastboot_bytes_expected = 0; |
||||||
|
fastboot_bytes_received = 0; |
||||||
|
} |
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
/**
|
||||||
|
* flash() - write the downloaded image to the indicated partition. |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to partition name |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Writes the previously downloaded image to the partition indicated by |
||||||
|
* cmd_parameter. Writes to response. |
||||||
|
*/ |
||||||
|
static void flash(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||||
|
fastboot_mmc_flash_write(cmd_parameter, fastboot_buf_addr, image_size, |
||||||
|
response); |
||||||
|
#endif |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) |
||||||
|
fastboot_nand_flash_write(cmd_parameter, fastboot_buf_addr, image_size, |
||||||
|
response); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* erase() - erase the indicated partition. |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to partition name |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Erases the partition indicated by cmd_parameter (clear to 0x00s). Writes |
||||||
|
* to response. |
||||||
|
*/ |
||||||
|
static void erase(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||||
|
fastboot_mmc_erase(cmd_parameter, response); |
||||||
|
#endif |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) |
||||||
|
fastboot_nand_erase(cmd_parameter, response); |
||||||
|
#endif |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* reboot_bootloader() - Sets reboot bootloader flag. |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to command parameter |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
*/ |
||||||
|
static void reboot_bootloader(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
if (fastboot_set_reboot_flag()) |
||||||
|
fastboot_fail("Cannot set reboot flag", response); |
||||||
|
else |
||||||
|
fastboot_okay(NULL, response); |
||||||
|
} |
@ -0,0 +1,230 @@ |
|||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <fastboot.h> |
||||||
|
#include <fastboot-internal.h> |
||||||
|
#include <fb_mmc.h> |
||||||
|
#include <fb_nand.h> |
||||||
|
#include <fs.h> |
||||||
|
#include <version.h> |
||||||
|
|
||||||
|
static void getvar_version(char *var_parameter, char *response); |
||||||
|
static void getvar_bootloader_version(char *var_parameter, char *response); |
||||||
|
static void getvar_downloadsize(char *var_parameter, char *response); |
||||||
|
static void getvar_serialno(char *var_parameter, char *response); |
||||||
|
static void getvar_version_baseband(char *var_parameter, char *response); |
||||||
|
static void getvar_product(char *var_parameter, char *response); |
||||||
|
static void getvar_current_slot(char *var_parameter, char *response); |
||||||
|
static void getvar_slot_suffixes(char *var_parameter, char *response); |
||||||
|
static void getvar_has_slot(char *var_parameter, char *response); |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||||
|
static void getvar_partition_type(char *part_name, char *response); |
||||||
|
#endif |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
static void getvar_partition_size(char *part_name, char *response); |
||||||
|
#endif |
||||||
|
|
||||||
|
static const struct { |
||||||
|
const char *variable; |
||||||
|
void (*dispatch)(char *var_parameter, char *response); |
||||||
|
} getvar_dispatch[] = { |
||||||
|
{ |
||||||
|
.variable = "version", |
||||||
|
.dispatch = getvar_version |
||||||
|
}, { |
||||||
|
.variable = "bootloader-version", |
||||||
|
.dispatch = getvar_bootloader_version |
||||||
|
}, { |
||||||
|
.variable = "version-bootloader", |
||||||
|
.dispatch = getvar_bootloader_version |
||||||
|
}, { |
||||||
|
.variable = "downloadsize", |
||||||
|
.dispatch = getvar_downloadsize |
||||||
|
}, { |
||||||
|
.variable = "max-download-size", |
||||||
|
.dispatch = getvar_downloadsize |
||||||
|
}, { |
||||||
|
.variable = "serialno", |
||||||
|
.dispatch = getvar_serialno |
||||||
|
}, { |
||||||
|
.variable = "version-baseband", |
||||||
|
.dispatch = getvar_version_baseband |
||||||
|
}, { |
||||||
|
.variable = "product", |
||||||
|
.dispatch = getvar_product |
||||||
|
}, { |
||||||
|
.variable = "current-slot", |
||||||
|
.dispatch = getvar_current_slot |
||||||
|
}, { |
||||||
|
.variable = "slot-suffixes", |
||||||
|
.dispatch = getvar_slot_suffixes |
||||||
|
}, { |
||||||
|
.variable = "has_slot", |
||||||
|
.dispatch = getvar_has_slot |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||||
|
}, { |
||||||
|
.variable = "partition-type", |
||||||
|
.dispatch = getvar_partition_type |
||||||
|
#endif |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
}, { |
||||||
|
.variable = "partition-size", |
||||||
|
.dispatch = getvar_partition_size |
||||||
|
#endif |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
static void getvar_version(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_okay(FASTBOOT_VERSION, response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_bootloader_version(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_okay(U_BOOT_VERSION, response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_downloadsize(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_response("OKAY", response, "0x%08x", fastboot_buf_size); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_serialno(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
const char *tmp = env_get("serial#"); |
||||||
|
|
||||||
|
if (tmp) |
||||||
|
fastboot_okay(tmp, response); |
||||||
|
else |
||||||
|
fastboot_fail("Value not set", response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_version_baseband(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_okay("N/A", response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_product(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
const char *board = env_get("board"); |
||||||
|
|
||||||
|
if (board) |
||||||
|
fastboot_okay(board, response); |
||||||
|
else |
||||||
|
fastboot_fail("Board not set", response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_current_slot(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
/* A/B not implemented, for now always return _a */ |
||||||
|
fastboot_okay("_a", response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_slot_suffixes(char *var_parameter, char *response) |
||||||
|
{ |
||||||
|
fastboot_okay("_a,_b", response); |
||||||
|
} |
||||||
|
|
||||||
|
static void getvar_has_slot(char *part_name, char *response) |
||||||
|
{ |
||||||
|
if (part_name && (!strcmp(part_name, "boot") || |
||||||
|
!strcmp(part_name, "system"))) |
||||||
|
fastboot_okay("yes", response); |
||||||
|
else |
||||||
|
fastboot_okay("no", response); |
||||||
|
} |
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||||
|
static void getvar_partition_type(char *part_name, char *response) |
||||||
|
{ |
||||||
|
int r; |
||||||
|
struct blk_desc *dev_desc; |
||||||
|
disk_partition_t part_info; |
||||||
|
|
||||||
|
r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, |
||||||
|
response); |
||||||
|
if (r >= 0) { |
||||||
|
r = fs_set_blk_dev_with_part(dev_desc, r); |
||||||
|
if (r < 0) |
||||||
|
fastboot_fail("failed to set partition", response); |
||||||
|
else |
||||||
|
fastboot_okay(fs_get_type_name(), response); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
static void getvar_partition_size(char *part_name, char *response) |
||||||
|
{ |
||||||
|
int r; |
||||||
|
size_t size; |
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_MMC) |
||||||
|
struct blk_desc *dev_desc; |
||||||
|
disk_partition_t part_info; |
||||||
|
|
||||||
|
r = fastboot_mmc_get_part_info(part_name, &dev_desc, &part_info, |
||||||
|
response); |
||||||
|
if (r >= 0) |
||||||
|
size = part_info.size; |
||||||
|
#endif |
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH_NAND) |
||||||
|
struct part_info *part_info; |
||||||
|
|
||||||
|
r = fastboot_nand_get_part_info(part_name, &part_info, response); |
||||||
|
if (r >= 0) |
||||||
|
size = part_info->size; |
||||||
|
#endif |
||||||
|
if (r >= 0) |
||||||
|
fastboot_response("OKAY", response, "0x%016zx", size); |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_getvar() - Writes variable indicated by cmd_parameter to response. |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to command parameter |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Look up cmd_parameter first as an environment variable of the form |
||||||
|
* fastboot.<cmd_parameter>, if that exists return use its value to set |
||||||
|
* response. |
||||||
|
* |
||||||
|
* Otherwise lookup the name of variable and execute the appropriate |
||||||
|
* function to return the requested value. |
||||||
|
*/ |
||||||
|
void fastboot_getvar(char *cmd_parameter, char *response) |
||||||
|
{ |
||||||
|
if (!cmd_parameter) { |
||||||
|
fastboot_fail("missing var", response); |
||||||
|
} else { |
||||||
|
#define FASTBOOT_ENV_PREFIX "fastboot." |
||||||
|
int i; |
||||||
|
char *var_parameter = cmd_parameter; |
||||||
|
char envstr[FASTBOOT_RESPONSE_LEN]; |
||||||
|
const char *s; |
||||||
|
|
||||||
|
snprintf(envstr, sizeof(envstr) - 1, |
||||||
|
FASTBOOT_ENV_PREFIX "%s", cmd_parameter); |
||||||
|
s = env_get(envstr); |
||||||
|
if (s) { |
||||||
|
fastboot_response("OKAY", response, "%s", s); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
strsep(&var_parameter, ":"); |
||||||
|
for (i = 0; i < ARRAY_SIZE(getvar_dispatch); ++i) { |
||||||
|
if (!strcmp(getvar_dispatch[i].variable, |
||||||
|
cmd_parameter)) { |
||||||
|
getvar_dispatch[i].dispatch(var_parameter, |
||||||
|
response); |
||||||
|
return; |
||||||
|
} |
||||||
|
} |
||||||
|
pr_warn("WARNING: unknown variable: %s\n", cmd_parameter); |
||||||
|
fastboot_fail("Variable not implemented", response); |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,36 @@ |
|||||||
|
/* SPDX-License-Identifier: GPL-2.0+ */ |
||||||
|
|
||||||
|
#ifndef _FASTBOOT_INTERNAL_H_ |
||||||
|
#define _FASTBOOT_INTERNAL_H_ |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_buf_addr - base address of the fastboot download buffer |
||||||
|
*/ |
||||||
|
extern void *fastboot_buf_addr; |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_buf_size - size of the fastboot download buffer |
||||||
|
*/ |
||||||
|
extern u32 fastboot_buf_size; |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_progress_callback - callback executed during long operations |
||||||
|
*/ |
||||||
|
extern void (*fastboot_progress_callback)(const char *msg); |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_getvar() - Writes variable indicated by cmd_parameter to response. |
||||||
|
* |
||||||
|
* @cmd_parameter: Pointer to command parameter |
||||||
|
* @response: Pointer to fastboot response buffer |
||||||
|
* |
||||||
|
* Look up cmd_parameter first as an environment variable of the form |
||||||
|
* fastboot.<cmd_parameter>, if that exists return use its value to set |
||||||
|
* response. |
||||||
|
* |
||||||
|
* Otherwise lookup the name of variable and execute the appropriate |
||||||
|
* function to return the requested value. |
||||||
|
*/ |
||||||
|
void fastboot_getvar(char *cmd_parameter, char *response); |
||||||
|
|
||||||
|
#endif |
@ -0,0 +1,21 @@ |
|||||||
|
/* SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
* |
||||||
|
* Copyright (C) 2016 The Android Open Source Project |
||||||
|
*/ |
||||||
|
|
||||||
|
#ifndef __NET_FASTBOOT_H__ |
||||||
|
#define __NET_FASTBOOT_H__ |
||||||
|
|
||||||
|
/**********************************************************************/ |
||||||
|
/*
|
||||||
|
* Global functions and variables. |
||||||
|
*/ |
||||||
|
|
||||||
|
/**
|
||||||
|
* Wait for incoming fastboot comands. |
||||||
|
*/ |
||||||
|
void fastboot_start_server(void); |
||||||
|
|
||||||
|
/**********************************************************************/ |
||||||
|
|
||||||
|
#endif /* __NET_FASTBOOT_H__ */ |
@ -0,0 +1,317 @@ |
|||||||
|
// SPDX-License-Identifier: BSD-2-Clause
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2016 The Android Open Source Project |
||||||
|
*/ |
||||||
|
|
||||||
|
#include <common.h> |
||||||
|
#include <fastboot.h> |
||||||
|
#include <net.h> |
||||||
|
#include <net/fastboot.h> |
||||||
|
|
||||||
|
/* Fastboot port # defined in spec */ |
||||||
|
#define WELL_KNOWN_PORT 5554 |
||||||
|
|
||||||
|
enum { |
||||||
|
FASTBOOT_ERROR = 0, |
||||||
|
FASTBOOT_QUERY = 1, |
||||||
|
FASTBOOT_INIT = 2, |
||||||
|
FASTBOOT_FASTBOOT = 3, |
||||||
|
}; |
||||||
|
|
||||||
|
struct __packed fastboot_header { |
||||||
|
uchar id; |
||||||
|
uchar flags; |
||||||
|
unsigned short seq; |
||||||
|
}; |
||||||
|
|
||||||
|
#define PACKET_SIZE 1024 |
||||||
|
#define DATA_SIZE (PACKET_SIZE - sizeof(struct fastboot_header)) |
||||||
|
|
||||||
|
/* Sequence number sent for every packet */ |
||||||
|
static unsigned short sequence_number = 1; |
||||||
|
static const unsigned short packet_size = PACKET_SIZE; |
||||||
|
static const unsigned short udp_version = 1; |
||||||
|
|
||||||
|
/* Keep track of last packet for resubmission */ |
||||||
|
static uchar last_packet[PACKET_SIZE]; |
||||||
|
static unsigned int last_packet_len; |
||||||
|
|
||||||
|
static struct in_addr fastboot_remote_ip; |
||||||
|
/* The UDP port at their end */ |
||||||
|
static int fastboot_remote_port; |
||||||
|
/* The UDP port at our end */ |
||||||
|
static int fastboot_our_port; |
||||||
|
|
||||||
|
static void boot_downloaded_image(void); |
||||||
|
|
||||||
|
#if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
||||||
|
/**
|
||||||
|
* fastboot_udp_send_info() - Send an INFO packet during long commands. |
||||||
|
* |
||||||
|
* @msg: String describing the reason for waiting |
||||||
|
*/ |
||||||
|
static void fastboot_udp_send_info(const char *msg) |
||||||
|
{ |
||||||
|
uchar *packet; |
||||||
|
uchar *packet_base; |
||||||
|
int len = 0; |
||||||
|
char response[FASTBOOT_RESPONSE_LEN] = {0}; |
||||||
|
|
||||||
|
struct fastboot_header response_header = { |
||||||
|
.id = FASTBOOT_FASTBOOT, |
||||||
|
.flags = 0, |
||||||
|
.seq = htons(sequence_number) |
||||||
|
}; |
||||||
|
++sequence_number; |
||||||
|
packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
||||||
|
packet_base = packet; |
||||||
|
|
||||||
|
/* Write headers */ |
||||||
|
memcpy(packet, &response_header, sizeof(response_header)); |
||||||
|
packet += sizeof(response_header); |
||||||
|
/* Write response */ |
||||||
|
fastboot_response("INFO", response, "%s", msg); |
||||||
|
memcpy(packet, response, strlen(response)); |
||||||
|
packet += strlen(response); |
||||||
|
|
||||||
|
len = packet - packet_base; |
||||||
|
|
||||||
|
/* Save packet for retransmitting */ |
||||||
|
last_packet_len = len; |
||||||
|
memcpy(last_packet, packet_base, last_packet_len); |
||||||
|
|
||||||
|
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
||||||
|
fastboot_remote_port, fastboot_our_port, len); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_timed_send_info() - Send INFO packet every 30 seconds |
||||||
|
* |
||||||
|
* @msg: String describing the reason for waiting |
||||||
|
* |
||||||
|
* Send an INFO packet during long commands based on timer. An INFO packet |
||||||
|
* is sent if the time is 30 seconds after start. Else, noop. |
||||||
|
*/ |
||||||
|
static void fastboot_timed_send_info(const char *msg) |
||||||
|
{ |
||||||
|
static ulong start; |
||||||
|
|
||||||
|
/* Initialize timer */ |
||||||
|
if (start == 0) |
||||||
|
start = get_timer(0); |
||||||
|
ulong time = get_timer(start); |
||||||
|
/* Send INFO packet to host every 30 seconds */ |
||||||
|
if (time >= 30000) { |
||||||
|
start = get_timer(0); |
||||||
|
fastboot_udp_send_info(msg); |
||||||
|
} |
||||||
|
} |
||||||
|
#endif |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_send() - Sends a packet in response to received fastboot packet |
||||||
|
* |
||||||
|
* @header: Header for response packet |
||||||
|
* @fastboot_data: Pointer to received fastboot data |
||||||
|
* @fastboot_data_len: Length of received fastboot data |
||||||
|
* @retransmit: Nonzero if sending last sent packet |
||||||
|
*/ |
||||||
|
static void fastboot_send(struct fastboot_header header, char *fastboot_data, |
||||||
|
unsigned int fastboot_data_len, uchar retransmit) |
||||||
|
{ |
||||||
|
uchar *packet; |
||||||
|
uchar *packet_base; |
||||||
|
int len = 0; |
||||||
|
const char *error_msg = "An error occurred."; |
||||||
|
short tmp; |
||||||
|
struct fastboot_header response_header = header; |
||||||
|
static char command[FASTBOOT_COMMAND_LEN]; |
||||||
|
static int cmd = -1; |
||||||
|
static bool pending_command; |
||||||
|
char response[FASTBOOT_RESPONSE_LEN] = {0}; |
||||||
|
|
||||||
|
/*
|
||||||
|
* We will always be sending some sort of packet, so |
||||||
|
* cobble together the packet headers now. |
||||||
|
*/ |
||||||
|
packet = net_tx_packet + net_eth_hdr_size() + IP_UDP_HDR_SIZE; |
||||||
|
packet_base = packet; |
||||||
|
|
||||||
|
/* Resend last packet */ |
||||||
|
if (retransmit) { |
||||||
|
memcpy(packet, last_packet, last_packet_len); |
||||||
|
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
||||||
|
fastboot_remote_port, fastboot_our_port, |
||||||
|
last_packet_len); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
response_header.seq = htons(response_header.seq); |
||||||
|
memcpy(packet, &response_header, sizeof(response_header)); |
||||||
|
packet += sizeof(response_header); |
||||||
|
|
||||||
|
switch (header.id) { |
||||||
|
case FASTBOOT_QUERY: |
||||||
|
tmp = htons(sequence_number); |
||||||
|
memcpy(packet, &tmp, sizeof(tmp)); |
||||||
|
packet += sizeof(tmp); |
||||||
|
break; |
||||||
|
case FASTBOOT_INIT: |
||||||
|
tmp = htons(udp_version); |
||||||
|
memcpy(packet, &tmp, sizeof(tmp)); |
||||||
|
packet += sizeof(tmp); |
||||||
|
tmp = htons(packet_size); |
||||||
|
memcpy(packet, &tmp, sizeof(tmp)); |
||||||
|
packet += sizeof(tmp); |
||||||
|
break; |
||||||
|
case FASTBOOT_ERROR: |
||||||
|
memcpy(packet, error_msg, strlen(error_msg)); |
||||||
|
packet += strlen(error_msg); |
||||||
|
break; |
||||||
|
case FASTBOOT_FASTBOOT: |
||||||
|
if (cmd == FASTBOOT_COMMAND_DOWNLOAD) { |
||||||
|
if (!fastboot_data_len && !fastboot_data_remaining()) { |
||||||
|
fastboot_data_complete(response); |
||||||
|
} else { |
||||||
|
fastboot_data_download(fastboot_data, |
||||||
|
fastboot_data_len, |
||||||
|
response); |
||||||
|
} |
||||||
|
} else if (!pending_command) { |
||||||
|
strlcpy(command, fastboot_data, |
||||||
|
min((size_t)fastboot_data_len + 1, |
||||||
|
sizeof(command))); |
||||||
|
pending_command = true; |
||||||
|
} else { |
||||||
|
cmd = fastboot_handle_command(command, response); |
||||||
|
pending_command = false; |
||||||
|
} |
||||||
|
/*
|
||||||
|
* Sent some INFO packets, need to update sequence number in |
||||||
|
* header |
||||||
|
*/ |
||||||
|
if (header.seq != sequence_number) { |
||||||
|
response_header.seq = htons(sequence_number); |
||||||
|
memcpy(packet_base, &response_header, |
||||||
|
sizeof(response_header)); |
||||||
|
} |
||||||
|
/* Write response to packet */ |
||||||
|
memcpy(packet, response, strlen(response)); |
||||||
|
packet += strlen(response); |
||||||
|
break; |
||||||
|
default: |
||||||
|
pr_err("ID %d not implemented.\n", header.id); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
len = packet - packet_base; |
||||||
|
|
||||||
|
/* Save packet for retransmitting */ |
||||||
|
last_packet_len = len; |
||||||
|
memcpy(last_packet, packet_base, last_packet_len); |
||||||
|
|
||||||
|
net_send_udp_packet(net_server_ethaddr, fastboot_remote_ip, |
||||||
|
fastboot_remote_port, fastboot_our_port, len); |
||||||
|
|
||||||
|
/* Continue boot process after sending response */ |
||||||
|
if (!strncmp("OKAY", response, 4)) { |
||||||
|
switch (cmd) { |
||||||
|
case FASTBOOT_COMMAND_BOOT: |
||||||
|
boot_downloaded_image(); |
||||||
|
break; |
||||||
|
|
||||||
|
case FASTBOOT_COMMAND_CONTINUE: |
||||||
|
net_set_state(NETLOOP_SUCCESS); |
||||||
|
break; |
||||||
|
|
||||||
|
case FASTBOOT_COMMAND_REBOOT: |
||||||
|
case FASTBOOT_COMMAND_REBOOT_BOOTLOADER: |
||||||
|
do_reset(NULL, 0, 0, NULL); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if (!strncmp("OKAY", response, 4) || !strncmp("FAIL", response, 4)) |
||||||
|
cmd = -1; |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* boot_downloaded_image() - Boots into downloaded image. |
||||||
|
*/ |
||||||
|
static void boot_downloaded_image(void) |
||||||
|
{ |
||||||
|
fastboot_boot(); |
||||||
|
net_set_state(NETLOOP_SUCCESS); |
||||||
|
} |
||||||
|
|
||||||
|
/**
|
||||||
|
* fastboot_handler() - Incoming UDP packet handler. |
||||||
|
* |
||||||
|
* @packet: Pointer to incoming UDP packet |
||||||
|
* @dport: Destination UDP port |
||||||
|
* @sip: Source IP address |
||||||
|
* @sport: Source UDP port |
||||||
|
* @len: Packet length |
||||||
|
*/ |
||||||
|
static void fastboot_handler(uchar *packet, unsigned int dport, |
||||||
|
struct in_addr sip, unsigned int sport, |
||||||
|
unsigned int len) |
||||||
|
{ |
||||||
|
struct fastboot_header header; |
||||||
|
char fastboot_data[DATA_SIZE] = {0}; |
||||||
|
unsigned int fastboot_data_len = 0; |
||||||
|
|
||||||
|
if (dport != fastboot_our_port) |
||||||
|
return; |
||||||
|
|
||||||
|
fastboot_remote_ip = sip; |
||||||
|
fastboot_remote_port = sport; |
||||||
|
|
||||||
|
if (len < sizeof(struct fastboot_header) || len > PACKET_SIZE) |
||||||
|
return; |
||||||
|
memcpy(&header, packet, sizeof(header)); |
||||||
|
header.flags = 0; |
||||||
|
header.seq = ntohs(header.seq); |
||||||
|
packet += sizeof(header); |
||||||
|
len -= sizeof(header); |
||||||
|
|
||||||
|
switch (header.id) { |
||||||
|
case FASTBOOT_QUERY: |
||||||
|
fastboot_send(header, fastboot_data, 0, 0); |
||||||
|
break; |
||||||
|
case FASTBOOT_INIT: |
||||||
|
case FASTBOOT_FASTBOOT: |
||||||
|
fastboot_data_len = len; |
||||||
|
if (len > 0) |
||||||
|
memcpy(fastboot_data, packet, len); |
||||||
|
if (header.seq == sequence_number) { |
||||||
|
fastboot_send(header, fastboot_data, |
||||||
|
fastboot_data_len, 0); |
||||||
|
sequence_number++; |
||||||
|
} else if (header.seq == sequence_number - 1) { |
||||||
|
/* Retransmit last sent packet */ |
||||||
|
fastboot_send(header, fastboot_data, |
||||||
|
fastboot_data_len, 1); |
||||||
|
} |
||||||
|
break; |
||||||
|
default: |
||||||
|
pr_err("ID %d not implemented.\n", header.id); |
||||||
|
header.id = FASTBOOT_ERROR; |
||||||
|
fastboot_send(header, fastboot_data, 0, 0); |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
void fastboot_start_server(void) |
||||||
|
{ |
||||||
|
printf("Using %s device\n", eth_get_name()); |
||||||
|
printf("Listening for fastboot command on %pI4\n", &net_ip); |
||||||
|
|
||||||
|
fastboot_our_port = WELL_KNOWN_PORT; |
||||||
|
|
||||||
|
fastboot_set_progress_callback(fastboot_timed_send_info); |
||||||
|
net_set_udp_handler(fastboot_handler); |
||||||
|
|
||||||
|
/* zero out server ether in case the server ip has changed */ |
||||||
|
memset(net_server_ethaddr, 0, 6); |
||||||
|
} |
Loading…
Reference in new issue