| // SPDX-License-Identifier: GPL-2.0+ |
| /* |
| * Copyright 2019 NXP |
| */ |
| |
| #include <asm/mach-imx/sys_proto.h> |
| #include <fb_fsl.h> |
| #include <fastboot.h> |
| #include <fastboot-internal.h> |
| #include <mmc.h> |
| #include <android_image.h> |
| #include <asm/bootm.h> |
| #include <nand.h> |
| #include <part.h> |
| #include <sparse_format.h> |
| #include <image-sparse.h> |
| #include <image.h> |
| #include <asm/mach-imx/boot_mode.h> |
| #include <asm/arch/sys_proto.h> |
| #include <asm/setup.h> |
| #include <environment.h> |
| #ifdef CONFIG_ANDROID_RECOVERY |
| #include <recovery.h> |
| #endif |
| |
| #ifdef CONFIG_BCB_SUPPORT |
| #include "bcb.h" |
| #endif |
| |
| #ifdef CONFIG_AVB_SUPPORT |
| #include <dt_table.h> |
| #include <fsl_avb.h> |
| #endif |
| |
| #ifdef CONFIG_ANDROID_THINGS_SUPPORT |
| #include <asm-generic/gpio.h> |
| #include <asm/mach-imx/gpio.h> |
| #include "../lib/avb/fsl/fsl_avbkey.h" |
| #include "../arch/arm/include/asm/mach-imx/hab.h" |
| #endif |
| |
| #if defined(CONFIG_FASTBOOT_LOCK) |
| #include "fastboot_lock_unlock.h" |
| #endif |
| |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| #include "u-boot/sha256.h" |
| #include <trusty/libtipc.h> |
| #endif |
| |
| #define EP_BUFFER_SIZE 4096 |
| |
| /** |
| * 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; |
| |
| #if defined(CONFIG_AVB_SUPPORT) && defined(CONFIG_MMC) |
| static AvbABOps fsl_avb_ab_ops = { |
| .read_ab_metadata = fsl_read_ab_metadata, |
| .write_ab_metadata = fsl_write_ab_metadata, |
| .ops = NULL |
| }; |
| #ifdef CONFIG_AVB_ATX |
| static AvbAtxOps fsl_avb_atx_ops = { |
| .ops = NULL, |
| .read_permanent_attributes = fsl_read_permanent_attributes, |
| .read_permanent_attributes_hash = fsl_read_permanent_attributes_hash, |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| .set_key_version = fsl_write_rollback_index_rpmb, |
| #else |
| .set_key_version = fsl_set_key_version, |
| #endif |
| .get_random = fsl_get_random |
| }; |
| #endif |
| static AvbOps fsl_avb_ops = { |
| .ab_ops = &fsl_avb_ab_ops, |
| #ifdef CONFIG_AVB_ATX |
| .atx_ops = &fsl_avb_atx_ops, |
| #endif |
| .read_from_partition = fsl_read_from_partition_multi, |
| .write_to_partition = fsl_write_to_partition, |
| #ifdef CONFIG_AVB_ATX |
| .validate_vbmeta_public_key = avb_atx_validate_vbmeta_public_key, |
| #else |
| .validate_vbmeta_public_key = fsl_validate_vbmeta_public_key_rpmb, |
| #endif |
| .read_rollback_index = fsl_read_rollback_index_rpmb, |
| .write_rollback_index = fsl_write_rollback_index_rpmb, |
| .read_is_device_unlocked = fsl_read_is_device_unlocked, |
| .get_unique_guid_for_partition = fsl_get_unique_guid_for_partition, |
| .get_size_of_partition = fsl_get_size_of_partition |
| }; |
| #endif |
| |
| |
| /* Write the bcb with fastboot bootloader commands */ |
| static void enable_fastboot_command(void) |
| { |
| #ifdef CONFIG_BCB_SUPPORT |
| char fastboot_command[32] = {0}; |
| strncpy(fastboot_command, FASTBOOT_BCB_CMD, 31); |
| bcb_write_command(fastboot_command); |
| #endif |
| } |
| |
| /* Get the Boot mode from BCB cmd or Key pressed */ |
| static FbBootMode fastboot_get_bootmode(void) |
| { |
| int boot_mode = BOOTMODE_NORMAL; |
| #ifdef CONFIG_ANDROID_RECOVERY |
| if(is_recovery_key_pressing()) { |
| boot_mode = BOOTMODE_RECOVERY_KEY_PRESSED; |
| return boot_mode; |
| } |
| #endif |
| #ifdef CONFIG_BCB_SUPPORT |
| int ret = 0; |
| char command[32]; |
| ret = bcb_read_command(command); |
| if (ret < 0) { |
| printf("read command failed\n"); |
| return boot_mode; |
| } |
| if (!strcmp(command, FASTBOOT_BCB_CMD)) { |
| boot_mode = BOOTMODE_FASTBOOT_BCB_CMD; |
| } |
| #ifdef CONFIG_ANDROID_RECOVERY |
| else if (!strcmp(command, RECOVERY_BCB_CMD)) { |
| boot_mode = BOOTMODE_RECOVERY_BCB_CMD; |
| } |
| #endif |
| |
| /* Clean the mode once its read out, |
| no matter what in the mode string */ |
| memset(command, 0, 32); |
| bcb_write_command(command); |
| #endif |
| return boot_mode; |
| } |
| |
| /* export to lib_arm/board.c */ |
| void fastboot_run_bootmode(void) |
| { |
| FbBootMode boot_mode = fastboot_get_bootmode(); |
| switch(boot_mode){ |
| case BOOTMODE_FASTBOOT_BCB_CMD: |
| /* Make the boot into fastboot mode*/ |
| puts("Fastboot: Got bootloader commands!\n"); |
| run_command("fastboot 0", 0); |
| break; |
| #ifdef CONFIG_ANDROID_RECOVERY |
| case BOOTMODE_RECOVERY_BCB_CMD: |
| case BOOTMODE_RECOVERY_KEY_PRESSED: |
| /* Make the boot into recovery mode */ |
| puts("Fastboot: Got Recovery key pressing or recovery commands!\n"); |
| board_recovery_setup(); |
| break; |
| #endif |
| default: |
| /* skip special mode boot*/ |
| puts("Fastboot: Normal\n"); |
| break; |
| } |
| } |
| |
| |
| |
| /** |
| * 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); |
| } |
| |
| /** |
| * 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) |
| { |
| enable_fastboot_command(); |
| |
| if (fastboot_set_reboot_flag()) |
| fastboot_fail("Cannot set reboot flag", response); |
| else |
| fastboot_okay(NULL, response); |
| } |
| |
| static void upload(char *cmd_parameter, char *response) |
| { |
| if (!fastboot_bytes_received || fastboot_bytes_received > (EP_BUFFER_SIZE * 32)) { |
| fastboot_fail("", response); |
| return; |
| } |
| |
| printf("Will upload %d bytes.\n", fastboot_bytes_received); |
| snprintf(response, FASTBOOT_RESPONSE_LEN, "DATA%08x", fastboot_bytes_received); |
| fastboot_tx_write_more(response); |
| |
| fastboot_tx_write((const char *)(fastboot_buf_addr), fastboot_bytes_received); |
| |
| snprintf(response,FASTBOOT_RESPONSE_LEN, "OKAY"); |
| fastboot_tx_write_more(response); |
| |
| fastboot_none_resp(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) |
| { |
| if (fastboot_bytes_received >= fastboot_bytes_expected) |
| return 0; |
| |
| 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); |
| env_set_hex("filesize", fastboot_bytes_received); |
| env_set_hex("fastboot_bytes", fastboot_bytes_received); |
| fastboot_bytes_expected = 0; |
| } |
| |
| #if defined(CONFIG_FASTBOOT_LOCK) |
| static int partition_table_valid(void) |
| { |
| int status, mmc_no; |
| struct blk_desc *dev_desc; |
| #if defined(CONFIG_IMX_TRUSTY_OS) && !defined(CONFIG_ARM64) |
| /* Prevent other partition accessing when no TOS flashed. */ |
| if (!tos_flashed) |
| return 0; |
| #endif |
| disk_partition_t info; |
| mmc_no = fastboot_devinfo.dev_id; |
| dev_desc = blk_get_dev("mmc", mmc_no); |
| if (dev_desc) |
| status = part_get_info(dev_desc, 1, &info); |
| else |
| status = -1; |
| return (status == 0); |
| } |
| |
| static void wipe_all_userdata(void) |
| { |
| char response[FASTBOOT_RESPONSE_LEN]; |
| |
| /* Erase all user data */ |
| printf("Start userdata wipe process....\n"); |
| /* Erase /data partition */ |
| fastboot_wipe_data_partition(); |
| |
| #if defined (CONFIG_ANDROID_SUPPORT) || defined (CONFIG_ANDROID_AUTO_SUPPORT) |
| /* Erase the misc partition. */ |
| process_erase_mmc(FASTBOOT_PARTITION_MISC, response); |
| #endif |
| |
| #ifndef CONFIG_ANDROID_AB_SUPPORT |
| /* Erase the cache partition for legacy imx6/7 */ |
| process_erase_mmc(FASTBOOT_PARTITION_CACHE, response); |
| #endif |
| /* The unlock permissive flag is set by user and should be wiped here. */ |
| set_fastboot_lock_disable(); |
| |
| |
| #if defined(AVB_RPMB) && !defined(CONFIG_IMX_TRUSTY_OS) |
| printf("Start stored_rollback_index wipe process....\n"); |
| rbkidx_erase(); |
| printf("Wipe stored_rollback_index completed.\n"); |
| #endif |
| printf("Wipe userdata completed.\n"); |
| } |
| |
| static FbLockState do_fastboot_unlock(bool force) |
| { |
| int status; |
| |
| if (fastboot_get_lock_stat() == FASTBOOT_UNLOCK) { |
| printf("The device is already unlocked\n"); |
| return FASTBOOT_UNLOCK; |
| } |
| if ((fastboot_lock_enable() == FASTBOOT_UL_ENABLE) || force) { |
| printf("It is able to unlock device. %d\n",fastboot_lock_enable()); |
| status = fastboot_set_lock_stat(FASTBOOT_UNLOCK); |
| if (status < 0) |
| return FASTBOOT_LOCK_ERROR; |
| |
| wipe_all_userdata(); |
| |
| } else { |
| printf("It is not able to unlock device."); |
| return FASTBOOT_LOCK_ERROR; |
| } |
| |
| return FASTBOOT_UNLOCK; |
| } |
| |
| static FbLockState do_fastboot_lock(void) |
| { |
| int status; |
| |
| if (fastboot_get_lock_stat() == FASTBOOT_LOCK) { |
| printf("The device is already locked\n"); |
| return FASTBOOT_LOCK; |
| } |
| status = fastboot_set_lock_stat(FASTBOOT_LOCK); |
| if (status < 0) |
| return FASTBOOT_LOCK_ERROR; |
| |
| wipe_all_userdata(); |
| |
| return FASTBOOT_LOCK; |
| } |
| |
| static bool endswith(char* s, char* subs) { |
| if (!s || !subs) |
| return false; |
| uint32_t len = strlen(s); |
| uint32_t sublen = strlen(subs); |
| if (len < sublen) { |
| return false; |
| } |
| if (strncmp(s + len - sublen, subs, sublen)) { |
| return false; |
| } |
| return true; |
| } |
| |
| static void flashing(char *cmd, char *response) |
| { |
| FbLockState status; |
| FbLockEnableResult result; |
| if (endswith(cmd, "lock_critical")) { |
| strcpy(response, "OKAY"); |
| } |
| #ifdef CONFIG_AVB_ATX |
| else if (endswith(cmd, FASTBOOT_AVB_AT_PERM_ATTR)) { |
| if (avb_atx_fuse_perm_attr(fastboot_buf_addr, fastboot_bytes_received)) |
| strcpy(response, "FAILInternal error!"); |
| else |
| strcpy(response, "OKAY"); |
| } else if (endswith(cmd, FASTBOOT_AT_GET_UNLOCK_CHALLENGE)) { |
| if (avb_atx_get_unlock_challenge(fsl_avb_ops.atx_ops, |
| fastboot_buf_addr, &fastboot_bytes_received)) |
| strcpy(response, "FAILInternal error!"); |
| else |
| strcpy(response, "OKAY"); |
| } else if (endswith(cmd, FASTBOOT_AT_UNLOCK_VBOOT)) { |
| if (at_unlock_vboot_is_disabled()) { |
| printf("unlock vboot already disabled, can't unlock the device!\n"); |
| strcpy(response, "FAILunlock vboot already disabled!."); |
| } else { |
| #ifdef CONFIG_AT_AUTHENTICATE_UNLOCK |
| if (avb_atx_verify_unlock_credential(fsl_avb_ops.atx_ops, |
| fastboot_buf_addr)) |
| strcpy(response, "FAILIncorrect unlock credential!"); |
| else { |
| #endif |
| status = do_fastboot_unlock(true); |
| if (status != FASTBOOT_LOCK_ERROR) |
| strcpy(response, "OKAY"); |
| else |
| strcpy(response, "FAILunlock device failed."); |
| #ifdef CONFIG_AT_AUTHENTICATE_UNLOCK |
| } |
| #endif |
| } |
| } else if (endswith(cmd, FASTBOOT_AT_LOCK_VBOOT)) { |
| if (perm_attr_are_fused()) { |
| status = do_fastboot_lock(); |
| if (status != FASTBOOT_LOCK_ERROR) |
| strcpy(response, "OKAY"); |
| else |
| strcpy(response, "FAILlock device failed."); |
| } else |
| strcpy(response, "FAILpermanent attributes not fused!"); |
| } else if (endswith(cmd, FASTBOOT_AT_DISABLE_UNLOCK_VBOOT)) { |
| /* This command can only be called after 'oem at-lock-vboot' */ |
| status = fastboot_get_lock_stat(); |
| if (status == FASTBOOT_LOCK) { |
| if (at_unlock_vboot_is_disabled()) { |
| printf("unlock vboot already disabled!\n"); |
| strcpy(response, "OKAY"); |
| } |
| else { |
| if (!at_disable_vboot_unlock()) |
| strcpy(response, "OKAY"); |
| else |
| strcpy(response, "FAILdisable unlock vboot fail!"); |
| } |
| } else |
| strcpy(response, "FAILplease lock the device first!"); |
| } |
| #endif /* CONFIG_AVB_ATX */ |
| #ifdef CONFIG_ANDROID_THINGS_SUPPORT |
| else if (endswith(cmd, FASTBOOT_BOOTLOADER_VBOOT_KEY)) { |
| strcpy(response, "OKAY"); |
| } |
| #endif /* CONFIG_ANDROID_THINGS_SUPPORT */ |
| #ifdef CONFIG_IMX_TRUSTY_OS |
| else if (endswith(cmd, FASTBOOT_GET_CA_REQ)) { |
| uint8_t *ca_output; |
| uint32_t ca_length, cp_length; |
| if (trusty_atap_get_ca_request(fastboot_buf_addr, fastboot_bytes_received, |
| &(ca_output), &ca_length)) { |
| printf("ERROR get_ca_request failed!\n"); |
| strcpy(response, "FAILInternal error!"); |
| } else { |
| cp_length = min((uint32_t)CONFIG_FASTBOOT_BUF_SIZE, ca_length); |
| memcpy(fastboot_buf_addr, ca_output, cp_length); |
| fastboot_bytes_received = ca_length; |
| strcpy(response, "OKAY"); |
| } |
| |
| } else if (endswith(cmd, FASTBOOT_SET_CA_RESP)) { |
| if (trusty_atap_set_ca_response(fastboot_buf_addr, fastboot_bytes_received)) { |
| printf("ERROR set_ca_response failed!\n"); |
| strcpy(response, "FAILInternal error!"); |
| } else |
| strcpy(response, "OKAY"); |
| } else if (endswith(cmd, FASTBOOT_SET_RSA_ATTESTATION_KEY)) { |
| if (trusty_set_attestation_key(fastboot_buf_addr, |
| fastboot_bytes_received, |
| KM_ALGORITHM_RSA)) { |
| printf("ERROR set rsa attestation key failed!\n"); |
| strcpy(response, "FAILInternal error!"); |
| } else { |
| printf("Set rsa attestation key successfully!\n"); |
| strcpy(response, "OKAY"); |
| } |
| } else if (endswith(cmd, FASTBOOT_SET_EC_ATTESTATION_KEY)) { |
| if (trusty_set_attestation_key(fastboot_buf_addr, |
| fastboot_bytes_received, |
| KM_ALGORITHM_EC)) { |
| printf("ERROR set ec attestation key failed!\n"); |
| strcpy(response, "FAILInternal error!"); |
| } else { |
| printf("Set ec attestation key successfully!\n"); |
| strcpy(response, "OKAY"); |
| } |
| } else if (endswith(cmd, FASTBOOT_APPEND_RSA_ATTESTATION_CERT)) { |
| if (trusty_append_attestation_cert_chain(fastboot_buf_addr, |
| fastboot_bytes_received, |
| KM_ALGORITHM_RSA)) { |
| printf("ERROR append rsa attestation cert chain failed!\n"); |
| strcpy(response, "FAILInternal error!"); |
| } else { |
| printf("Append rsa attestation key successfully!\n"); |
| strcpy(response, "OKAY"); |
| } |
| } else if (endswith(cmd, FASTBOOT_APPEND_EC_ATTESTATION_CERT)) { |
| if (trusty_append_attestation_cert_chain(fastboot_buf_addr, |
| fastboot_bytes_received, |
| KM_ALGORITHM_EC)) { |
| printf("ERROR append ec attestation cert chain failed!\n"); |
| strcpy(response, "FAILInternal error!"); |
| } else { |
| printf("Append ec attestation key successfully!\n"); |
| strcpy(response, "OKAY"); |
| } |
| } |
| #ifndef CONFIG_AVB_ATX |
| else if (endswith(cmd, FASTBOOT_SET_RPMB_KEY)) { |
| if (fastboot_set_rpmb_key(fastboot_buf_addr, fastboot_bytes_received)) { |
| printf("ERROR set rpmb key failed!\n"); |
| strcpy(response, "FAILset rpmb key failed!"); |
| } else |
| strcpy(response, "OKAY"); |
| } else if (endswith(cmd, FASTBOOT_SET_RPMB_RANDOM_KEY)) { |
| if (fastboot_set_rpmb_random_key()) { |
| printf("ERROR set rpmb random key failed!\n"); |
| strcpy(response, "FAILset rpmb random key failed!"); |
| } else |
| strcpy(response, "OKAY"); |
| } else if (endswith(cmd, FASTBOOT_SET_VBMETA_PUBLIC_KEY)) { |
| if (avb_set_public_key(fastboot_buf_addr, |
| fastboot_bytes_received)) |
| strcpy(response, "FAILcan't set public key!"); |
| else |
| strcpy(response, "OKAY"); |
| } |
| #endif /* !CONFIG_AVB_ATX */ |
| #endif /* CONFIG_IMX_TRUSTY_OS */ |
| else if (endswith(cmd, "unlock_critical")) { |
| strcpy(response, "OKAY"); |
| } else if (endswith(cmd, "unlock")) { |
| printf("flashing unlock.\n"); |
| #ifdef CONFIG_AVB_ATX |
| /* We should do nothing here For Android Things which |
| * enables the authenticated unlock feature. |
| */ |
| strcpy(response, "OKAY"); |
| #else |
| status = do_fastboot_unlock(false); |
| if (status != FASTBOOT_LOCK_ERROR) |
| strcpy(response, "OKAY"); |
| else |
| strcpy(response, "FAILunlock device failed."); |
| #endif |
| } else if (endswith(cmd, "lock")) { |
| #ifdef CONFIG_AVB_ATX |
| /* We should do nothing here For Android Things which |
| * enables the at-lock-vboot feature. |
| */ |
| strcpy(response, "OKAY"); |
| #else |
| printf("flashing lock.\n"); |
| status = do_fastboot_lock(); |
| if (status != FASTBOOT_LOCK_ERROR) |
| strcpy(response, "OKAY"); |
| else |
| strcpy(response, "FAILlock device failed."); |
| #endif |
| } else if (endswith(cmd, "get_unlock_ability")) { |
| result = fastboot_lock_enable(); |
| if (result == FASTBOOT_UL_ENABLE) { |
| fastboot_tx_write_more("INFO1"); |
| strcpy(response, "OKAY"); |
| } else if (result == FASTBOOT_UL_DISABLE) { |
| fastboot_tx_write_more("INFO0"); |
| strcpy(response, "OKAY"); |
| } else { |
| printf("flashing get_unlock_ability fail!\n"); |
| strcpy(response, "FAILget unlock ability failed."); |
| } |
| } else { |
| printf("Unknown flashing command:%s\n", cmd); |
| strcpy(response, "FAILcommand not defined"); |
| } |
| fastboot_tx_write_more(response); |
| |
| /* Must call fastboot_none_resp before returning from the dispatch function |
| * which uses fastboot_tx_write_more |
| */ |
| fastboot_none_resp(response); |
| } |
| #endif /* CONFIG_FASTBOOT_LOCK */ |
| |
| #ifdef CONFIG_AVB_SUPPORT |
| static void set_active_avb(char *cmd, char *response) |
| { |
| AvbIOResult ret; |
| int slot = 0; |
| |
| if (!cmd) { |
| pr_err("missing slot suffix\n"); |
| fastboot_fail("missing slot suffix", response); |
| return; |
| } |
| |
| slot = slotidx_from_suffix(cmd); |
| |
| if (slot < 0) { |
| fastboot_fail("err slot suffix", response); |
| return; |
| } |
| |
| ret = avb_ab_mark_slot_active(&fsl_avb_ab_ops, slot); |
| if (ret != AVB_IO_RESULT_OK) |
| fastboot_fail("avb IO error", response); |
| else |
| fastboot_okay(NULL, response); |
| |
| return; |
| } |
| #endif /*CONFIG_AVB_SUPPORT*/ |
| |
| #if CONFIG_IS_ENABLED(FASTBOOT_FLASH) |
| static void flash(char *cmd, char *response) |
| { |
| if (!cmd) { |
| pr_err("missing partition name"); |
| fastboot_fail("missing partition name", response); |
| return; |
| } |
| |
| /* Always enable image flash for Android Things. */ |
| #if defined(CONFIG_FASTBOOT_LOCK) && !defined(CONFIG_AVB_ATX) |
| int status; |
| status = fastboot_get_lock_stat(); |
| |
| if (status == FASTBOOT_LOCK) { |
| pr_err("device is LOCKed!\n"); |
| fastboot_fail("device is locked.", response); |
| return; |
| |
| } else if (status == FASTBOOT_LOCK_ERROR) { |
| pr_err("write lock status into device!\n"); |
| fastboot_set_lock_stat(FASTBOOT_LOCK); |
| fastboot_fail("device is locked.", response); |
| return; |
| } |
| #endif |
| |
| fastboot_process_flash(cmd, fastboot_buf_addr, |
| fastboot_bytes_received, response); |
| |
| #if defined(CONFIG_FASTBOOT_LOCK) |
| if (strncmp(cmd, "gpt", 3) == 0) { |
| int gpt_valid = 0; |
| gpt_valid = partition_table_valid(); |
| /* If gpt is valid, load partitons table into memory. |
| So if the next command is "fastboot reboot bootloader", |
| it can find the "misc" partition to r/w. */ |
| if(gpt_valid) { |
| fastboot_load_partitions(); |
| /* Unlock device if the gpt is valid */ |
| do_fastboot_unlock(true); |
| } |
| } |
| |
| #endif |
| } |
| |
| static void erase(char *cmd, char *response) |
| { |
| if (!cmd) { |
| pr_err("missing partition name"); |
| fastboot_fail("missing partition name", response); |
| return; |
| } |
| |
| #if defined(CONFIG_FASTBOOT_LOCK) && !defined(CONFIG_AVB_ATX) |
| FbLockState status; |
| status = fastboot_get_lock_stat(); |
| if (status == FASTBOOT_LOCK) { |
| pr_err("device is LOCKed!\n"); |
| fastboot_fail("device is locked.", response); |
| return; |
| } else if (status == FASTBOOT_LOCK_ERROR) { |
| pr_err("write lock status into device!\n"); |
| fastboot_set_lock_stat(FASTBOOT_LOCK); |
| fastboot_fail("device is locked.", response); |
| return; |
| } |
| #endif |
| fastboot_process_erase(cmd, response); |
| } |
| #endif |
| |
| #if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT) |
| /** |
| * run_ucmd() - Execute the UCmd command |
| * |
| * @cmd_parameter: Pointer to command parameter |
| * @response: Pointer to fastboot response buffer |
| */ |
| static void run_ucmd(char *cmd_parameter, char *response) |
| { |
| if (!cmd_parameter) { |
| pr_err("missing slot suffix\n"); |
| fastboot_fail("missing command", response); |
| return; |
| } |
| if(run_command(cmd_parameter, 0)) { |
| fastboot_fail("", response); |
| } else { |
| fastboot_okay(NULL, response); |
| /* cmd may impact fastboot related environment*/ |
| fastboot_setup(); |
| } |
| } |
| |
| static char g_a_cmd_buff[64]; |
| |
| void fastboot_acmd_complete(void) |
| { |
| run_command(g_a_cmd_buff, 0); |
| } |
| |
| /** |
| * run_acmd() - Execute the ACmd command |
| * |
| * @cmd_parameter: Pointer to command parameter |
| * @response: Pointer to fastboot response buffer |
| */ |
| static void run_acmd(char *cmd_parameter, char *response) |
| { |
| if (!cmd_parameter) { |
| pr_err("missing slot suffix\n"); |
| fastboot_fail("missing command", response); |
| return; |
| } |
| strcpy(g_a_cmd_buff, cmd_parameter); |
| fastboot_okay(NULL, response); |
| } |
| #endif |
| |
| static const struct { |
| const char *command; |
| void (*dispatch)(char *cmd_parameter, char *response); |
| } commands[FASTBOOT_COMMAND_COUNT] = { |
| [FASTBOOT_COMMAND_REBOOT_BOOTLOADER] = { |
| .command = "reboot-bootloader", |
| .dispatch = reboot_bootloader, |
| }, |
| [FASTBOOT_COMMAND_UPLOAD] = { |
| .command = "upload", |
| .dispatch = upload, |
| }, |
| [FASTBOOT_COMMAND_GETSTAGED] = { |
| .command = "get_staged", |
| .dispatch = upload, |
| }, |
| #if defined(CONFIG_FASTBOOT_LOCK) |
| [FASTBOOT_COMMAND_FLASHING] = { |
| .command = "flashing", |
| .dispatch = flashing, |
| }, |
| [FASTBOOT_COMMAND_OEM] = { |
| .command = "oem", |
| .dispatch = flashing, |
| }, |
| #endif |
| #ifdef CONFIG_AVB_SUPPORT |
| [FASTBOOT_COMMAND_SETACTIVE] = { |
| .command = "set_active", |
| .dispatch = set_active_avb, |
| }, |
| #endif |
| #if CONFIG_IS_ENABLED(FASTBOOT_UUU_SUPPORT) |
| [FASTBOOT_COMMAND_UCMD] = { |
| .command = "UCmd", |
| .dispatch = run_ucmd, |
| }, |
| [FASTBOOT_COMMAND_ACMD] = { |
| .command ="ACmd", |
| .dispatch = run_acmd, |
| }, |
| #endif |
| [FASTBOOT_COMMAND_REBOOT] = { |
| .command = "reboot", |
| .dispatch = okay, |
| }, |
| [FASTBOOT_COMMAND_GETVAR] = { |
| .command = "getvar", |
| .dispatch = getvar, |
| }, |
| [FASTBOOT_COMMAND_DOWNLOAD] = { |
| .command = "download", |
| .dispatch = download, |
| }, |
| [FASTBOOT_COMMAND_BOOT] = { |
| .command = "boot", |
| .dispatch = okay, |
| }, |
| [FASTBOOT_COMMAND_CONTINUE] = { |
| .command = "continue", |
| .dispatch = okay, |
| }, |
| #ifdef CONFIG_FASTBOOT_FLASH |
| [FASTBOOT_COMMAND_FLASH] = { |
| .command = "flash", |
| .dispatch = flash, |
| }, |
| [FASTBOOT_COMMAND_ERASE] = { |
| .command = "erase", |
| .dispatch = erase, |
| }, |
| #endif |
| #ifdef CONFIG_AVB_ATX |
| [FASTBOOT_COMMAND_STAGE] = { |
| .command = "stage", |
| .dispatch = download, |
| }, |
| #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 < ARRAY_SIZE(commands); i++) { |
| if (commands[i].command != NULL && |
| !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; |
| } |