| // SPDX-License-Identifier: BSD-2-Clause | |
| /* | |
| * Copyright (C) 2016 The Android Open Source Project | |
| */ | |
| #include <common.h> | |
| #include <env.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 *); | |
| #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) | |
| static void oem_format(char *, char *); | |
| #endif | |
| 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 | |
| }, | |
| #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) | |
| [FASTBOOT_COMMAND_OEM_FORMAT] = { | |
| .command = "oem format", | |
| .dispatch = oem_format, | |
| }, | |
| #endif | |
| }; | |
| /** | |
| * 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); | |
| } | |
| #if CONFIG_IS_ENABLED(FASTBOOT_CMD_OEM_FORMAT) | |
| /** | |
| * oem_format() - Execute the OEM format command | |
| * | |
| * @cmd_parameter: Pointer to command parameter | |
| * @response: Pointer to fastboot response buffer | |
| */ | |
| static void oem_format(char *cmd_parameter, char *response) | |
| { | |
| char cmdbuf[32]; | |
| if (!env_get("partitions")) { | |
| fastboot_fail("partitions not set", response); | |
| } else { | |
| sprintf(cmdbuf, "gpt write mmc %x $partitions", | |
| CONFIG_FASTBOOT_FLASH_MMC_DEV); | |
| if (run_command(cmdbuf, 0)) | |
| fastboot_fail("", response); | |
| else | |
| fastboot_okay(NULL, response); | |
| } | |
| } | |
| #endif |