// SPDX-License-Identifier: (GPL-2.0-only OR MIT) /* * Copyright (C) 2025 Amlogic, Inc. All rights reserved */ #include #include #include #include #include #include #include #include #include #include #include #include <../../drivers/usb/gadget/v2_burning/v2_common/optimus_download.h> #include <../../drivers/usb/gadget/v2_burning/v2_burning_i.h> #include #include #ifdef CONFIG_DDR_FASTBOOT #include #endif #include #if defined(CONFIG_AML_ANTIROLLBACK) || defined(CONFIG_AML_AVB2_ANTIROLLBACK) #include #endif #define PARTNAME_MAX_LEN 128 /* UDISK package format for xxx_udisk.img */ #define UDISK_PKG_MAGIC "UDISKPKG" #define UDISK_PKG_MAGIC_SIZE 8 #define UDISK_PKG_HEADER_SIZE 64 #define UDISK_PKG_IMG_OFFSET (64 << 10) /* 64KB */ #define MAX_IMAGE_NAMES 16 #define MAX_IMAGE_NAME_LEN 64 #define INI_BUFFER_SIZE 1024 #define BOOTLOADER_OFFSET 512 #define BOOTLOADER_MAX_SIZE (4 * 1024 * 1024) /* HMAC-SHA256 security */ #define HMAC_SHA256_SIZE 32 #define HMAC_KEY_SIZE 32 #define HMAC_IMG_DATA_MAX_SIZE (1 << 20) /* 1MB - max img data for HMAC */ /* * HMAC key storage (obfuscated) * The actual key is computed at runtime by XORing these parts * Key = part1 ^ part2 ^ part3 */ static const u8 hmac_key_part1[HMAC_KEY_SIZE] = { 0x4A, 0x8B, 0x2C, 0x5D, 0x9E, 0x1F, 0x6A, 0x3B, 0x7C, 0x0D, 0xBE, 0x4F, 0xE0, 0x81, 0x22, 0x63, 0xD4, 0x95, 0x56, 0x17, 0xA8, 0xC9, 0x7A, 0x2B, 0x8C, 0x4D, 0xEE, 0x8F, 0x30, 0xD1, 0x62, 0xB3}; static const u8 hmac_key_part2[HMAC_KEY_SIZE] = { 0x1D, 0xCE, 0x7F, 0x20, 0xE1, 0x52, 0x33, 0x74, 0x25, 0x46, 0xF7, 0x18, 0xB9, 0xDA, 0x6B, 0x2C, 0x9D, 0xDE, 0x0F, 0x50, 0xF1, 0x82, 0x23, 0x74, 0xC5, 0x06, 0xB7, 0xC8, 0x79, 0x8A, 0x3B, 0xEC}; static const u8 hmac_key_part3[HMAC_KEY_SIZE] = { 0x73, 0x24, 0x95, 0xE6, 0x07, 0xB8, 0xC9, 0x1A, 0xCB, 0xAC, 0x1D, 0xF6, 0x57, 0x30, 0x81, 0xC2, 0x53, 0x34, 0xE5, 0xBE, 0x1F, 0x68, 0xC9, 0x9A, 0x2B, 0xEC, 0x5D, 0x26, 0x97, 0x60, 0xD1, 0x02}; /* Compute the actual HMAC key at runtime */ static void get_hmac_key(u8 *key) { int i; for (i = 0; i < HMAC_KEY_SIZE; i++) key[i] = hmac_key_part1[i] ^ hmac_key_part2[i] ^ hmac_key_part3[i]; } /* Simple HMAC-SHA256 implementation using U-Boot's SHA256 */ static void hmac_sha256(const u8 *key, size_t key_len, const u8 *data, size_t data_len, u8 *out) { sha256_context ctx; u8 k_ipad[64]; u8 k_opad[64]; u8 tk[32]; u8 inner_hash[32]; int i; /* If key is longer than 64 bytes, hash it first */ if (key_len > 64) { sha256_starts(&ctx); sha256_update(&ctx, key, key_len); sha256_finish(&ctx, tk); key = tk; key_len = 32; } /* Prepare ipad and opad */ memset(k_ipad, 0x36, 64); memset(k_opad, 0x5c, 64); for (i = 0; i < (int)key_len; i++) { k_ipad[i] ^= key[i]; k_opad[i] ^= key[i]; } /* Inner hash: SHA256(k_ipad || data) */ sha256_starts(&ctx); sha256_update(&ctx, k_ipad, 64); sha256_update(&ctx, data, data_len); sha256_finish(&ctx, inner_hash); /* Outer hash: SHA256(k_opad || inner_hash) */ sha256_starts(&ctx); sha256_update(&ctx, k_opad, 64); sha256_update(&ctx, inner_hash, 32); sha256_finish(&ctx, out); } typedef struct { char magic[UDISK_PKG_MAGIC_SIZE]; /* "UDISKPKG" */ u32 vendor_info_offset; u32 vendor_info_size; u32 avb_key_offset; u32 avb_key_size; u32 img_offset; u32 img_size; u32 hmac_offset; /* HMAC-SHA256 offset */ u32 hmac_size; /* HMAC-SHA256 size (32 bytes) */ u8 reserved[24]; } __attribute__((packed)) udisk_pkg_header_t; #ifdef CONFIG_AVB2_KPUB_VENDOR extern const char avb2_kpub_vendor[]; extern const int avb2_kpub_vendor_len; #endif /* CONFIG_AVB_KPUB_VENDOR */ extern const char avb2_kpub_default[]; extern const int avb2_kpub_default_len; extern const char avb2_kpub_production[]; extern const int avb2_kpub_production_len; const char *const UpgradeLogo_Addr = (const char *)(OPTIMUS_DOWNLOAD_DISPLAY_BUF + OPTIMUS_DOWNLOAD_SLOT_SZ); #ifdef CONFIG_REPLACE_TV_MODE /* Upgrade file names */ static const char *const big_pkg_files[] = { "aml_upgrade_package_all.img", "aml_upgrade_package_no_oem_key.img", "aml_upgrade_package_no_tvconfig_oem.img", "aml_upgrade_package_no_tvconfig_oem_key.img", "aml_upgrade_package_no_tvconfig_oem_key_pid.img", "aml_upgrade_package_no_tvconfig_oem_skw_key_pid.img", "aml_upgrade_package.img", NULL }; static const char *const small_pkg_files[] = { "oem_udisk.img", "tvconfig_udisk.img", "skw_udisk.img", "bootloader_udisk.img", "vbmeta_udisk.img", NULL }; #define MAX_SMALL_PKGS 3 #define MAX_FILENAME_LEN 128 #define PID_PREFIX "pid_index_" /* Structure to hold scan results */ typedef struct { int big_pkg_count; char big_pkg_name[MAX_FILENAME_LEN]; int big_pkg_index; /* Index in big_pkg_files array */ int small_pkg_count; char small_pkg_names[MAX_SMALL_PKGS][MAX_FILENAME_LEN]; int pid_count; char pid_name[MAX_FILENAME_LEN]; } usb_upgrade_files_t; /** * scan_usb_upgrade_files - Scan USB root directory for upgrade files * @files: Output structure to hold scan results * * Uses fatls to list all files in USB root, then matches against known * upgrade file names. This is more efficient than checking each file * individually with fatsize. * * Return: 0 on success, -1 on failure */ static int scan_usb_upgrade_files(usb_upgrade_files_t *files) { int ret, i; struct fs_dir_stream *dirs; struct fs_dirent *dent; if (!files) return -1; memset(files, 0, sizeof(*files)); /* First, check if USB is ready */ ret = run_command("usb start 0", 0); if (ret != 0) { printf("USB start failed\n"); return -1; } /* Set block device for fs operations */ if (fs_set_blk_dev("usb", "0", FS_TYPE_FAT)) { printf("Failed to set usb device\n"); return -1; } /* Open root directory */ dirs = fs_opendir("/"); if (!dirs) { printf("Failed to open USB root directory\n"); return -1; } /* Iterate through directory entries */ while ((dent = fs_readdir(dirs)) != NULL) { /* Skip directories */ if (dent->type == FS_DT_DIR) continue; const char *name = dent->name; /* Check big packages */ for (i = 0; big_pkg_files[i]; i++) { if (strcasecmp(name, big_pkg_files[i]) == 0) { files->big_pkg_count++; if (files->big_pkg_count == 1) { strncpy(files->big_pkg_name, name, MAX_FILENAME_LEN - 1); files->big_pkg_index = i; } printf("Found big package: %s\n", name); break; } } /* Check small packages */ for (i = 0; small_pkg_files[i]; i++) { if (strcasecmp(name, small_pkg_files[i]) == 0) { if (files->small_pkg_count < MAX_SMALL_PKGS) { strncpy(files->small_pkg_names[files->small_pkg_count], name, MAX_FILENAME_LEN - 1); files->small_pkg_count++; } printf("Found small package: %s\n", name); break; } } /* Check PID files (pid_index_*.img pattern) */ if (strncasecmp(name, PID_PREFIX, strlen(PID_PREFIX)) == 0) { char *ext = strstr(name, ".img"); if (ext && (ext[4] == '\0' || ext[4] == ' ')) { if (files->pid_count == 0) strncpy(files->pid_name, name, MAX_FILENAME_LEN - 1); files->pid_count++; printf("Found PID file: %s\n", name); } } } fs_closedir(dirs); printf("Scan complete: big_pkg=%d, small_pkg=%d, pid=%d\n", files->big_pkg_count, files->small_pkg_count, files->pid_count); return 0; } /** * generate_ini_file - Generate aml_sdc_burn.ini on USB disk * @pkg_name: Name of the upgrade package file * * Creates the INI file required for USB burning. * * Return: 0 on success, -1 on failure */ static int generate_ini_file(const char *pkg_name) { char cmd[256]; ulong loadaddr; char *buf; int size; int ret; if (!pkg_name) return -1; loadaddr = (ulong)env_get_hex("loadaddr", 0x4000); buf = (char *)loadaddr; /* Generate INI content */ size = sprintf( buf, ";\n" ";Amlogic sdcard burning configure script\n" ";This card burning script support both dos and unix file format\n" ";Except comment, all must readable ASCII letters\n" ";\n" "\n" "[common]\n" "erase_bootloader = 0\n" "erase_flash = 1\n" "reboot = 1\n" "\n" ";package will filled by sdcard burning tool\n" "[burn_ex]\n" "package = %s\n" ";media =\n", pkg_name); printf("Generating aml_sdc_burn.ini for package: %s\n", pkg_name); /* Write INI file to USB */ sprintf(cmd, "fatwrite usb 0 %lx aml_sdc_burn.ini %x", loadaddr, size); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to write aml_sdc_burn.ini to USB\n"); return -1; } printf("Successfully created aml_sdc_burn.ini\n"); return 0; } int aml_udisk_lock(int state) { char *rpmb_flag = env_get("rpmb_state"); if (rpmb_flag && strcmp(rpmb_flag, "0")) { char *lock_s = env_get("lock"); LockData_t info = { 0 }; int erase_flag = 0; char lock_d[LOCK_DATA_SIZE]; memset(&info, 0, sizeof(struct LockData)); info.version_major = (int)(lock_s[0] - '0'); info.version_minor = (int)(lock_s[1] - '0'); info.unlock_ability = (int)(lock_s[2] - '0'); info.lock_state = (int)(lock_s[4] - '0'); info.lock_critical_state = (int)(lock_s[5] - '0'); info.lock_bootloader = (int)(lock_s[6] - '0'); #if defined(CONFIG_AML_ANTIROLLBACK) || defined(CONFIG_AML_AVB2_ANTIROLLBACK) uint32_t lock_state = 0; printf("avb arb mode\n"); if (is_avb_arb_available()) { if (get_avb_lock_state(&lock_state)) { if (state == 0 && (info.lock_state == 1 || lock_state == 1)) { printf("device is locked, need unlock\n"); if (avb_unlock()) erase_flag = 1; } else if (state == 1 && (info.lock_state == 0 || lock_state == 0)) { printf("device is unlocked, need lock\n"); if (avb_lock()) erase_flag = 1; } } } #endif if (state == 0 && info.lock_state == 1) { printf("device is locked, need unlock\n"); info.lock_state = 0; info.lock_critical_state = 0; erase_flag = 1; env_set("lock_state", "green"); sprintf(lock_d, "%d%d%d0%d%d%d0", info.version_major, info.version_minor, info.unlock_ability, info.lock_state, info.lock_critical_state, info.lock_bootloader); printf("lock_d state: %s\n", lock_d); env_set("lock", lock_d); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p lock;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } else if (state == 1 && info.lock_state == 0) { printf("device is unlocked, need lock\n"); info.lock_state = 1; erase_flag = 1; env_set("lock_state", "orange"); sprintf(lock_d, "%d%d%d0%d%d%d0", info.version_major, info.version_minor, info.unlock_ability, info.lock_state, info.lock_critical_state, info.lock_bootloader); printf("lock_d state: %s\n", lock_d); env_set("lock", lock_d); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p lock;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } if (erase_flag == 1) run_command("store erase metadata 0 0", 0); } return 0; } int check_pid_update_udisk(const char *file_name, int show_flag); int check_udisk_burn_fw(usb_upgrade_files_t *files) { int ret = 0; ulong loadaddr, tmp_addr; char cmd[128]; int erase_all_flag = 0; int erase_pid_flag = 0; int erase_key_flag = 0; char pid[50] = {0x0}; char mac[50] = {0x0}; char usid[50] = {0x0}; int ret_key = 0; int len = 0; const char *target_img = NULL; if (!files || files->big_pkg_count != 1) { printf("No big package to upgrade\n"); return -1; } loadaddr = (ulong)env_get_hex("loadaddr", 0x4000); tmp_addr = loadaddr - 0x1000; printf("upgrading %s\n", files->big_pkg_name); /* Generate INI file for this package */ ret = generate_ini_file(files->big_pkg_name); if (ret != 0) { printf("Failed to generate INI file\n"); return -1; } target_img = files->big_pkg_name; /* Set erase flags based on package index */ if (files->big_pkg_index == 0) erase_all_flag = 1; if (files->big_pkg_index >= 1 && files->big_pkg_index <= 3) erase_pid_flag = 1; if (files->big_pkg_index == 2) erase_key_flag = 1; /* Verify the image exists and load it */ sprintf(cmd, "fatload usb 0 %lx %s 4", loadaddr, target_img); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load %s\n", target_img); return -1; } printf("read aml_upgrade_package.img from udisk ok\n"); printf("need burn\n"); if (erase_all_flag == 1 || erase_key_flag == 1) { env_set("need_erase_key", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p need_erase_key;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } if (erase_all_flag == 1) { printf("erase all unifykey\n"); run_command("mmc dev 1", 0); run_command("emmc erase key", 0); env_set("udisk_burn_state", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p udisk_burn_state;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } if (erase_pid_flag == 1) { printf("erase pid\n"); if (key_unify_read("model_name", pid, sizeof(pid)) == 0) { debug("key_unify_read pid ok, reset it\n"); run_command("defenv_reserv; setenv reboot_mode cold_boot; saveenv;", 0); char *model = env_get("model_name"); printf("model is %s\n", model); len = strlen(model) + 1; ret_key = key_unify_write("model_name", model, len); if (ret_key == 0) { printf("write default pid ok, erase param\n"); run_command("store erase param 0 0", 0); } } else { printf("key_unify_read pid error, skip\n"); } if (files->pid_count > 0 && files->pid_name[0] != '\0') { printf("found valid pid file %s, trying to update pid via udisk package\n", files->pid_name); int pid_ret = check_pid_update_udisk(files->pid_name, 0); if (pid_ret == 0) { printf("write pid from %s ok\n", files->pid_name); } else { printf("write pid from %s failed\n", files->pid_name); } } } if (erase_key_flag == 1) { printf("delete mac\n"); if (key_unify_read("mac", mac, sizeof(mac)) == 0) { debug("key_unify_read mac ok, reset it\n"); memset(mac, 0, sizeof(mac)); strcpy(mac, "00:00:00:00:00:00"); len = strlen("00:00:00:00:00:00") + 1; ret_key = key_unify_write("mac", mac, len); if (ret_key == 0) debug("write default mac ok\n"); } else { printf("key_unify_read mac error, skip\n"); } printf("delete usid\n"); if (key_unify_read("usid", usid, sizeof(usid)) == 0) { debug("key_unify_read usid ok, reset it\n"); memset(usid, 0, sizeof(usid)); strcpy(usid, "0000"); len = strlen("0000") + 1; ret_key = key_unify_write("usid", usid, len); if (ret_key == 0) debug("write default usid ok\n"); } else { printf("key_unify_read usid error, skip\n"); } } //erase rollback index aml_udisk_lock(0); aml_udisk_lock(1); env_set("usb_burn_state", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p usb_burn_state;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif ret = run_command("usb_burn aml_sdc_burn.ini", 0); printf("ret = %d\n", ret); if (ret != 0) { env_set("usb_burn_state", "2"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p usb_burn_state;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif run_command("reboot", 0); } return ret; } int read_file_part(const char *filename, unsigned long offset, unsigned long len, unsigned long buf_addr) { char cmd[128]; int ret; sprintf(cmd, "fatload usb 0 %lx %s %lx %lx", buf_addr, filename, len, offset); ret = run_command(cmd, 0); unsigned long actual_len = env_get_hex("filesize", 0); return (ret == 0 && actual_len == len) ? 0 : -1; } /** * verify_udisk_pkg_key - Verify AVB public key in udisk package * @pkg_addr: Address where the udisk package is loaded * @header: Pointer to the parsed header * * Return: 0 if key matches, -1 otherwise */ static int verify_udisk_pkg_key(ulong pkg_addr, udisk_pkg_header_t *header) { const char *public_key_data; int public_key_length; int ret = -1; public_key_data = (const char *)(pkg_addr + header->avb_key_offset); public_key_length = header->avb_key_size; printf("UDISK package AVB key: size=%d\n", public_key_length); #ifdef CONFIG_AVB2_KPUB_VENDOR if (avb2_kpub_vendor_len == public_key_length && !memcmp(public_key_data, avb2_kpub_vendor, avb2_kpub_vendor_len)) { printf("udisk key match vendor kpub\n"); ret = 0; } #ifdef CONFIG_TESTKEY if (ret != AVB_IO_RESULT_OK) { printf("re-verify with default kpub\n"); if (avb2_kpub_default_len == public_key_length && !memcmp(public_key_data, avb2_kpub_default, public_key_length)) { printf("udisk key match default kpub\n"); ret = 0; } } #endif if (ret != 0) printf("udisk key NOT match vendor kpub!\n"); return ret; #else printf("not use vendor kpub, skip check\n"); ret = 0; #endif return ret; } /** * verify_udisk_pkg_hmac - Verify HMAC-SHA256 of udisk package * @pkg_addr: Address where the udisk package is loaded * @header: Pointer to the parsed header * @img_data: Pointer to the original image data * @img_size: Size of the original image data * * HMAC is computed over: vendor_info + avb_key + img_data * * Return: 0 if HMAC matches, -1 otherwise */ static int verify_udisk_pkg_hmac(ulong pkg_addr, udisk_pkg_header_t *header, const u8 *img_data, u32 img_size) { u8 hmac_key[HMAC_KEY_SIZE]; u8 computed_hmac[HMAC_SHA256_SIZE]; const u8 *stored_hmac; u8 *hmac_data = NULL; size_t hmac_data_len; const u8 *vendor_info; const u8 *avb_key; int ret = -1; /* Check if HMAC is present */ if (header->hmac_size != HMAC_SHA256_SIZE || header->hmac_offset == 0) { printf("HMAC not present in package (size=%u, offset=%u)\n", header->hmac_size, header->hmac_offset); return -1; } /* Get the stored HMAC from package */ stored_hmac = (const u8 *)(pkg_addr + header->hmac_offset); /* Get pointers to data for HMAC computation */ vendor_info = (const u8 *)(pkg_addr + header->vendor_info_offset); avb_key = (const u8 *)(pkg_addr + header->avb_key_offset); /* Allocate buffer for HMAC data: vendor_info + avb_key + img_data */ hmac_data_len = header->vendor_info_size + header->avb_key_size + img_size; hmac_data = (u8 *)malloc(hmac_data_len); if (!hmac_data) { printf("Failed to allocate HMAC data buffer\n"); return -1; } /* Copy data for HMAC computation */ memcpy(hmac_data, vendor_info, header->vendor_info_size); memcpy(hmac_data + header->vendor_info_size, avb_key, header->avb_key_size); memcpy(hmac_data + header->vendor_info_size + header->avb_key_size, img_data, img_size); /* Get the HMAC key (computed at runtime from obfuscated parts) */ get_hmac_key(hmac_key); /* Compute HMAC */ hmac_sha256(hmac_key, HMAC_KEY_SIZE, hmac_data, hmac_data_len, computed_hmac); /* Compare with stored HMAC */ if (memcmp(computed_hmac, stored_hmac, HMAC_SHA256_SIZE) == 0) { printf("HMAC verification passed\n"); ret = 0; } else { printf("HMAC verification FAILED! Package may be tampered.\n"); ret = -1; } /* Clear sensitive data */ memset(hmac_key, 0, HMAC_KEY_SIZE); free(hmac_data); return ret; } /** * parse_udisk_vendor_info - Parse and print vendor info from udisk package * @pkg_addr: Address where the udisk package is loaded * @header: Pointer to the parsed header * @vendor_device: Output buffer for vendor_device value (must be at least 64 * bytes) * * Return: 0 on success, -1 on failure */ static int parse_udisk_vendor_info(ulong pkg_addr, udisk_pkg_header_t *header, char *vendor_device, size_t device_buf_size) { char *vendor_info; char *line, *end; int found_device = 0; if (!vendor_device || device_buf_size == 0) return -1; vendor_device[0] = '\0'; vendor_info = (char *)(pkg_addr + header->vendor_info_offset); debug("=== UDISK Package Vendor Info ===\n"); /* Parse each line of vendor info */ line = vendor_info; end = vendor_info + header->vendor_info_size; while (line < end && *line) { char *newline = line; char *eq_pos; while (newline < end && *newline && *newline != '\n') newline++; if (newline > line) { /* Temporarily null-terminate */ char saved = *newline; *newline = '\0'; debug(" %s\n", line); /* Parse key=value */ eq_pos = strchr(line, '='); if (eq_pos) { *eq_pos = '\0'; if (strcmp(line, "vendor_device") == 0) { strncpy(vendor_device, eq_pos + 1, device_buf_size - 1); vendor_device[device_buf_size - 1] = '\0'; found_device = 1; } *eq_pos = '='; } *newline = saved; } line = newline + 1; } debug("=================================\n"); if (!found_device) { printf("Warning: vendor_device not found in vendor info\n"); return -1; } printf("Parsed vendor_device: %s\n", vendor_device); return 0; } /** * check_partition_update - Check and update partition from USB disk * @partition_name: Name of partition to update (e.g., "oem", "odm_ext") * * This function loads the partition image (xxx.img or xxx_udisk.img as passed), * and if it's a udisk package, verifies the AVB key and board name match * before burning. Only burns if all verifications pass. * * Return: 0 on success, -1 on failure */ int check_partition_update(const char *partition_name) { char partname[128] = {0}; char imgname[128] = {0}; char cmd[256] = {0}; char *slot_name; int ret = -1; ulong loadaddr; ulong pkg_addr; u64 rc = 0; udisk_pkg_header_t *header; char vendor_device[64] = {0}; char *board_name; slot_name = env_get("slot-suffixes"); loadaddr = (ulong)env_get_hex("loadaddr", 0x4000); pkg_addr = loadaddr; sprintf(imgname, "%s.img", partition_name); ret = run_command("usb start 0", 0); if (ret != 0) { printf("USB start failed\n"); return -1; } /* Check if the image file exists */ env_set_hex("filesize", 0); sprintf(cmd, "fatsize usb 0 %s", imgname); ret = run_command(cmd, 0); if (env_get_hex("filesize", 0) == 0) { printf("%s not found on USB disk\n", imgname); return -1; } printf("Found %s (size=0x%x), loading header first...\n", imgname, UDISK_PKG_HEADER_SIZE); /* Load the image file */ sprintf(cmd, "fatload usb 0 %lx %s %x", pkg_addr, imgname, UDISK_PKG_HEADER_SIZE); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load header of %s\n", imgname); return -1; } /* Check if this is a UDISK package (by checking magic) */ header = (udisk_pkg_header_t *)pkg_addr; if (memcmp(header->magic, UDISK_PKG_MAGIC, UDISK_PKG_MAGIC_SIZE) == 0) { printf("Detected UDISK package format\n"); debug("UDISK package header:\n"); debug(" vendor_info: offset=%u, size=%u\n", header->vendor_info_offset, header->vendor_info_size); debug(" avb_key: offset=%u, size=%u\n", header->avb_key_offset, header->avb_key_size); debug(" image: offset=%u, size=%u\n", header->img_offset, header->img_size); /* Load header + vendor_info + avb_key for verification */ ulong verify_size = header->img_offset; debug("Loading verification data (size=0x%lx)...\n", verify_size); sprintf(cmd, "fatload usb 0 %lx %s %lx", pkg_addr, imgname, verify_size); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load verification data\n"); return -1; } /* Parse vendor info and get vendor_device */ ret = parse_udisk_vendor_info(pkg_addr, header, vendor_device, sizeof(vendor_device)); if (ret != 0) { printf("Failed to parse vendor info, abort upgrade\n"); return -1; } /* Verify board name matches vendor_device */ board_name = env_get("board"); debug("Board name from env: %s\n", board_name ? board_name : "NULL"); debug("Vendor device from package: %s\n", vendor_device); if (!board_name || strcmp(board_name, vendor_device) != 0) { printf("ERROR: Board name mismatch! board=%s, vendor_device=%s\n", board_name ? board_name : "NULL", vendor_device); printf("Abort upgrade for safety\n"); return -1; } printf("Board name matched: %s\n", board_name); /* Verify AVB key */ ret = verify_udisk_pkg_key(pkg_addr, header); if (ret != 0) { printf("AVB key verification failed, abort upgrade\n"); return -1; } /* Verify HMAC-SHA256 - only use first 1MB of img data for large images */ ulong hmac_verify_addr = loadaddr + UDISK_PKG_IMG_OFFSET; ulong img_size_for_hmac = header->img_size; /* For large images, only verify first 1MB */ if (img_size_for_hmac > HMAC_IMG_DATA_MAX_SIZE) { img_size_for_hmac = HMAC_IMG_DATA_MAX_SIZE; printf("Large image detected, HMAC will verify first 1MB only\n"); } printf("Loading image for HMAC verification (size=0x%lx)...\n", img_size_for_hmac); sprintf(cmd, "fatload usb 0 %lx %s %lx %x", hmac_verify_addr, imgname, img_size_for_hmac, header->img_offset); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load image for HMAC verification\n"); return -1; } ret = verify_udisk_pkg_hmac( pkg_addr, header, (const u8 *)hmac_verify_addr, img_size_for_hmac); if (ret != 0) { printf("HMAC verification failed, abort upgrade\n"); return -1; } } else { printf("invalid image\n"); return -1; } /* Remove "_udisk" suffix from partition_name if present */ char real_partname[128] = {0}; /* Handle special image name mappings: * tvconfig_udisk -> odm_ext * skw_udisk -> production_ext */ if (strncmp(partition_name, "tvconfig_udisk", 14) == 0) { strncpy(real_partname, "odm_ext", sizeof(real_partname) - 1); } else if (strncmp(partition_name, "skw_udisk", 9) == 0) { strncpy(real_partname, "production_ext", sizeof(real_partname) - 1); } else { /* Remove "_udisk" suffix from partition_name if present */ strncpy(real_partname, partition_name, sizeof(real_partname) - 1); char *udisk_suffix = strstr(real_partname, "_udisk"); if (udisk_suffix && udisk_suffix[6] == '\0') *udisk_suffix = '\0'; } rc = store_part_size(real_partname); if (-1 == rc) { printf("try ab mode\n"); if (slot_name && (strcmp(slot_name, "0") == 0)) sprintf(partname, "%s_a", real_partname); else if (slot_name && (strcmp(slot_name, "1") == 0)) sprintf(partname, "%s_b", real_partname); } else { sprintf(partname, "%s", real_partname); } debug("key matched, proceeding with burn\n"); //ulong img_addr = pkg_addr + header->img_offset; ulong img_size = header->img_size; ulong img_offset = header->img_offset; ulong chunk_size = UDISK_PKG_IMG_OFFSET; /* 64KB per chunk */ ulong bytes_written = 0; ulong bytes_remaining = img_size; ulong current_offset = img_offset; __hdle hUiProgress = NULL; int logo_flag = -1; int rcode = 0; printf("Burning %s from UDISK package (offset=0x%lx, size=0x%lx)...\n", real_partname, img_offset, img_size); debug("Using 64KB chunk transfer\n"); /*show ui*/ if (video_res_prepare_for_upgrade(NULL)) { printf("Fail when prepare bm res or init video for upgrade\n"); logo_flag = -1; goto _update; } show_logo_to_report_burning(); hUiProgress = optimus_progress_ui_request_for_sdc_burn(); if (!hUiProgress) { printf("request progress handle failed!\n"); logo_flag = -1; goto _update; } optimus_progress_ui_direct_update_progress(hUiProgress, UPGRADE_STEPS_AFTER_DISK_INIT_OK); if (strncmp(partname, "bootloader", 10) == 0) { rcode = optimus_progress_ui_set_smart_mode(hUiProgress, 200, UPGRADE_STEPS_AFTER_BURN_BOOTLOADER_OK - UPGRADE_STEPS_AFTER_DISK_INIT_OK); run_command("sleep 6", 0); if (rcode) optimus_progress_ui_update_by_bytes(hUiProgress, 200); } else { rcode = optimus_progress_ui_set_smart_mode(hUiProgress, img_size, UPGRADE_STEPS_AFTER_BURN_BOOTLOADER_OK - UPGRADE_STEPS_AFTER_DISK_INIT_OK); } if (rcode) { printf("Fail to set smart mode\n"); logo_flag = -1; goto _update; } logo_flag = 0; _update: /* Read and write in 64KB chunks */ if (strncmp(partname, "bootloader", 10) == 0) { printf("write bootloader\n"); sprintf(cmd, "fatload usb 0 %lx %s %lx %lx", loadaddr, imgname, img_size, img_offset); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load chunk at offset 0x%lx\n", img_offset); return -1; } ret = store_boot_write("bootloader", 1, BOOTLOADER_MAX_SIZE - BOOTLOADER_OFFSET, (void *)loadaddr); if (ret != 0) { printf("Failed to write boot0\n"); return -1; } printf("write boot0 ok\n"); ret = store_boot_write("bootloader", 2, BOOTLOADER_MAX_SIZE - BOOTLOADER_OFFSET, (void *)loadaddr); if (ret != 0) { printf("Failed to write boot0\n"); return -1; } printf("write boot1 ok\n"); run_command("mmc dev 1 0;", 0); if (hUiProgress) optimus_progress_ui_report_upgrade_stat(hUiProgress, !rcode); if (hUiProgress) optimus_progress_ui_release(hUiProgress); run_command("sleep 2", 0); } else { printf("write other part: %s\n", partname); while (bytes_remaining > 0) { ulong read_size = (bytes_remaining >= chunk_size) ? chunk_size : bytes_remaining; /* Load 64KB (or remaining) chunk from USB */ sprintf(cmd, "fatload usb 0 %lx %s %lx %lx", loadaddr, imgname, read_size, current_offset); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load chunk at offset 0x%lx\n", current_offset); return -1; } /* Write chunk to storage using store_logic_write */ ret = store_logic_write(partname, bytes_written, read_size, (void *)loadaddr); if (ret != 0) { printf("Failed to write chunk at offset 0x%lx\n", bytes_written); return -1; } if (logo_flag == 0) optimus_progress_ui_update_by_bytes(hUiProgress, read_size); bytes_written += read_size; bytes_remaining -= read_size; current_offset += read_size; /* Print progress every 1MB */ if ((bytes_written % (1 << 20)) == 0 || bytes_remaining == 0) { printf("Progress: %lu / %lu bytes (%lu%%)\n", bytes_written, img_size, (bytes_written * 100) / img_size); } } if (hUiProgress) optimus_progress_ui_report_upgrade_stat(hUiProgress, !rcode); if (hUiProgress) optimus_progress_ui_release(hUiProgress); } printf("Successfully wrote %lu bytes to %s\n", bytes_written, partname); ret = 0; return ret; } /** * check_pid_update_udisk - Check and update PID from UDISK package format * @file_name: Name of the PID package file (e.g., "pid_2001.img") * * This function loads the PID package, verifies board name and AVB key, * then extracts and writes the PID value. * * Return: 0 on success, -1 on failure */ int check_pid_update_udisk(const char *file_name, int show_flag) { ulong loadaddr; ulong pkg_addr; int ret = -1; char cmd[256]; char pid[50] = {0x0}; udisk_pkg_header_t *header; char vendor_device[64] = {0}; char *board_name; loadaddr = (ulong)env_get_hex("loadaddr", 0x4000); pkg_addr = loadaddr; ret = run_command("usb start 0", 0); if (ret != 0) { printf("USB start failed\n"); return -1; } /* Check if the PID package file exists */ env_set_hex("filesize", 0); sprintf(cmd, "fatsize usb 0 %s", file_name); ret = run_command(cmd, 0); if (env_get_hex("filesize", 0) == 0) { printf("%s not found on USB disk\n", file_name); return -1; } ulong file_size = env_get_hex("filesize", 0); printf("Found %s (size=0x%lx)\n", file_name, file_size); /* Load header first (64 bytes) */ sprintf(cmd, "fatload usb 0 %lx %s %x", pkg_addr, file_name, UDISK_PKG_HEADER_SIZE); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load header of %s\n", file_name); return -1; } /* Check if this is a UDISK package */ header = (udisk_pkg_header_t *)pkg_addr; if (memcmp(header->magic, UDISK_PKG_MAGIC, UDISK_PKG_MAGIC_SIZE) != 0) { printf("%s is not a UDISK package format\n", file_name); return -1; } printf("Detected UDISK PID package format\n"); /* Load header + vendor_info + avb_key for verification */ ulong verify_size = header->img_offset; sprintf(cmd, "fatload usb 0 %lx %s %lx", pkg_addr, file_name, verify_size); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load verification data\n"); return -1; } /* Parse vendor info and get vendor_device */ ret = parse_udisk_vendor_info(pkg_addr, header, vendor_device, sizeof(vendor_device)); if (ret != 0) { printf("Failed to parse vendor info, abort PID update\n"); return -1; } /* Verify board name matches vendor_device */ board_name = env_get("board"); if (!board_name || strcmp(board_name, vendor_device) != 0) { printf("ERROR: Board name mismatch! board=%s, vendor_device=%s\n", board_name ? board_name : "NULL", vendor_device); printf("Abort PID update for safety\n"); return -1; } printf("Board name matched: %s\n", board_name); /* Verify AVB key */ ret = verify_udisk_pkg_key(pkg_addr, header); if (ret != 0) { printf("AVB key verification failed, abort PID update\n"); return -1; } /* Load PID data from package */ ulong pid_offset = header->img_offset; ulong pid_size = header->img_size; /* Load PID data to correct offset to avoid overwriting header */ ulong pid_load_addr = pkg_addr + pid_offset; sprintf(cmd, "fatload usb 0 %lx %s %lx %lx", pid_load_addr, file_name, pid_size, pid_offset); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load PID data\n"); return -1; } /* Verify HMAC-SHA256 */ ret = verify_udisk_pkg_hmac(pkg_addr, header, (const u8 *)pid_load_addr, pid_size); if (ret != 0) { printf("HMAC verification failed, abort PID update\n"); return -1; } char *src = (char *)pid_load_addr; size_t src_max_len = pid_size; char *line_end = src; while (line_end < src + src_max_len && *line_end != '\n' && *line_end != '\r') line_end++; char *start = src; while (start < line_end && isspace((unsigned char)*start)) start++; char *end = line_end - 1; while (end > start && isspace((unsigned char)*end)) end--; size_t valid_len = (start <= end) ? (end - start + 1) : 0; if (valid_len > sizeof(pid) - 1) valid_len = sizeof(pid) - 1; if (valid_len > 0) memcpy(pid, start, valid_len); pid[valid_len] = '\0'; printf("PID value from package: [%s]\n", pid); /* Write PID to key storage */ ret = key_unify_write("model_name", pid, strlen(pid) + 1); if (ret == 0) { printf("write pid ok, actual pid: [%s]\n", pid); env_set("model_name", pid); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p model_name;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } else { printf("write pid failed, actual pid: [%s]\n", pid); } if (show_flag == 1) { __hdle hUiProgress = NULL; int rcode = 0; if (video_res_prepare_for_upgrade(NULL)) { printf("Fail when prepare bm res or init video for upgrade\n"); goto _update; } show_logo_to_report_burning(); hUiProgress = optimus_progress_ui_request_for_sdc_burn(); if (!hUiProgress) { printf("request progress handle failed!\n"); goto _update; } optimus_progress_ui_direct_update_progress(hUiProgress, UPGRADE_STEPS_AFTER_DISK_INIT_OK); rcode = optimus_progress_ui_set_smart_mode(hUiProgress, 200, UPGRADE_STEPS_AFTER_BURN_BOOTLOADER_OK - UPGRADE_STEPS_AFTER_DISK_INIT_OK); if (rcode) { printf("Fail to set smart mode\n"); goto _update; } run_command("sleep 6", 0); optimus_progress_ui_update_by_bytes(hUiProgress, 200); _update: if (hUiProgress) optimus_progress_ui_report_upgrade_stat(hUiProgress, !rcode); if (hUiProgress) optimus_progress_ui_release(hUiProgress); } return ret; } /** * parse_image_names_and_update - Parse image_name from ini file and update * partitions * * This function reads the aml_sdc_burn.ini file from USB disk, parses the * "image_name" line (format: image_name = oem_udisk.img:odm_ext_udisk.img:...), * extracts partition names (removing .img suffix), and calls * check_partition_update for each. * * @odm_ext_flag: Output parameter, set to 0 if odm_ext was successfully updated * @logo_update_flag: Output parameter, set to 0 if oem or production_ext was * successfully updated * * Return: Number of partitions successfully updated, or -1 on error */ int parse_image_names_and_update(int *odm_ext_flag, int *logo_update_flag, int *oem_flag) { const char *ini_file = "aml_sdc_burn.ini"; char cmd[256] = {0}; char *ini_buffer = NULL; char *line_start, *line_end, *value_start; char partition_names[MAX_IMAGE_NAMES][MAX_IMAGE_NAME_LEN]; int partition_count = 0; int update_count = 0; int ret = -1; ulong loadaddr; ulong file_size; /* Initialize output flags to -1 (not updated) */ if (odm_ext_flag) *odm_ext_flag = -1; if (logo_update_flag) *logo_update_flag = -1; if (oem_flag) *oem_flag = -1; loadaddr = (ulong)env_get_hex("loadaddr", 0x4000); /* Check if ini file exists and get its size */ ret = run_command("usb start 0", 0); if (ret != 0) { printf("USB start failed\n"); return -1; } env_set_hex("filesize", 0); sprintf(cmd, "fatsize usb 0 %s", ini_file); ret = run_command(cmd, 0); file_size = env_get_hex("filesize", 0); if (file_size == 0) { printf("%s not found or empty\n", ini_file); return -1; } /* Limit the read size to avoid large allocations */ if (file_size > INI_BUFFER_SIZE) file_size = INI_BUFFER_SIZE; /* Load ini file content */ sprintf(cmd, "fatload usb 0 %lx %s %lx", loadaddr, ini_file, file_size); ret = run_command(cmd, 0); if (ret != 0) { printf("Failed to load %s\n", ini_file); return -1; } ini_buffer = (char *)loadaddr; ini_buffer[file_size] = '\0'; /* Search for image_name line */ line_start = ini_buffer; while (line_start < ini_buffer + file_size) { /* Find end of current line */ line_end = line_start; while (line_end < ini_buffer + file_size && *line_end != '\n' && *line_end != '\r') line_end++; /* Temporarily null terminate the line */ char saved_char = *line_end; *line_end = '\0'; /* Check if this line starts with "image_name" (skip whitespace) */ char *check = line_start; while (*check && isspace((unsigned char)*check)) check++; if (strncmp(check, "image_name", 10) == 0) { /* Find '=' sign */ value_start = strchr(check, '='); if (value_start) { value_start++; /* Skip '=' */ /* Skip whitespace after '=' */ while (*value_start && isspace((unsigned char)*value_start)) value_start++; /* Parse separated image names */ char *token_start = value_start; char *token_end; while (*token_start && partition_count < MAX_IMAGE_NAMES) { /* Skip leading whitespace */ while (*token_start && isspace((unsigned char)*token_start)) token_start++; if (*token_start == '\0') break; /* Find comma or end of string */ token_end = token_start; while (*token_end && *token_end != ':') token_end++; /* Copy token (image filename) */ size_t token_len = token_end - token_start; /* Trim trailing whitespace */ while (token_len > 0 && isspace((unsigned char)token_start[token_len - 1])) token_len--; if (token_len > 0 && token_len < MAX_IMAGE_NAME_LEN) { char img_name[MAX_IMAGE_NAME_LEN]; memcpy(img_name, token_start, token_len); img_name[token_len] = '\0'; /* Remove .img suffix to get partition name */ char *suffix = strstr(img_name, ".img"); if (suffix && suffix[4] == '\0') *suffix = '\0'; if (strncmp(img_name, "pid_", 4) == 0) { printf("Skipping PID file in image_name: %s (use pid_name " "instead)\n", img_name); } else { /* Store partition name */ strncpy(partition_names[partition_count], img_name, MAX_IMAGE_NAME_LEN - 1); partition_names[partition_count][MAX_IMAGE_NAME_LEN - 1] = '\0'; printf("Found partition to update: %s\n", partition_names[partition_count]); partition_count++; } } /* Move to next token */ if (*token_end == ':') token_start = token_end + 1; else break; } } *line_end = saved_char; break; } *line_end = saved_char; /* Restore */ /* Move to next line */ line_start = line_end; while (line_start < ini_buffer + file_size && (*line_start == '\n' || *line_start == '\r')) line_start++; } if (partition_count == 0) { printf("No image_name entry found in %s\n", ini_file); return 0; } /* Call check_partition_update for each partition */ printf("Updating %d partitions from ini file...\n", partition_count); for (int i = 0; i < partition_count; i++) { printf("Checking partition update for: %s\n", partition_names[i]); ret = check_partition_update(partition_names[i]); if (ret == 0) { printf("Successfully updated partition: %s\n", partition_names[i]); update_count++; /* Set appropriate flag based on partition name prefix */ if (strncmp(partition_names[i], "odm_ext", 7) == 0 || strncmp(partition_names[i], "tvconfig_udisk", 14) == 0) { if (odm_ext_flag) *odm_ext_flag = 0; } else if (strncmp(partition_names[i], "oem", 3) == 0 || strncmp(partition_names[i], "production_ext", 14) == 0 || strncmp(partition_names[i], "bootloader_udisk", 16) == 0 || strncmp(partition_names[i], "skw_udisk", 9) == 0) { if (logo_update_flag) *logo_update_flag = 0; } if (strncmp(partition_names[i], "oem", 3) == 0) if (oem_flag) *oem_flag = 0; } else { printf("Partition %s update skipped or failed\n", partition_names[i]); } } return update_count; } typedef struct { const char *bmp_filename; const char *env_prefix; } BmpFileInfo; static const BmpFileInfo bmp_files_list[] = { {"upgrade_error.bmp", "upgrade_error"}, {"upgrade_bar.bmp", "upgrade_bar"}, {"upgrade_fail.bmp", "upgrade_fail"}, {"upgrade_unfocus.bmp", "upgrade_unfocus"}, {"upgrade_success.bmp", "upgrade_success"}, {"upgrade_upgrading.bmp", "upgrade_upgrading"} }; #define BMP_FILES_CNT (sizeof(bmp_files_list) / sizeof(BmpFileInfo)) int prepare_display(void) { char cmd[256] = {0}; static const char *bmpsLoadAddr = (const char *)UpgradeLogo_Addr; char partname[PARTNAME_MAX_LEN] = {0}; char env_data[PARTNAME_MAX_LEN] = {0}; char *slot_name; const char *suffix = NULL; const char *board_logo_part = env_get("board_logo_part"); int ret = 0; int partIndex; ulong file_size = 0; int i; unsigned long current_load_addr = (unsigned long)bmpsLoadAddr; if (board_logo_part && *board_logo_part != '\0') strcpy((char *)partname, board_logo_part); else strcpy((char *)partname, "oem"); partname[PARTNAME_MAX_LEN - 1] = '\0'; run_command("printenv bootLogoPart;", 0); partIndex = get_partition_num_by_name(env_get("bootLogoPart")); printf("partIndex = %d\n", partIndex); if (partIndex <= 0) { printf("fail find part index for name(%s), try ab\n", env_get("bootLogoPart")); slot_name = env_get("slot-suffixes"); if (slot_name && (strcmp(slot_name, "0") == 0)) suffix = "_a"; else if (slot_name && (strcmp(slot_name, "1") == 0)) suffix = "_b"; if (suffix != NULL) { size_t remaining = PARTNAME_MAX_LEN - strlen(partname) - 1; if (remaining > 0) strncat(partname, suffix, remaining); else return -1; } partIndex = get_partition_num_by_name(partname); if (partIndex < 0) { printf("Error: get partition index failed\n"); return -1; } } for (i = 0; i < BMP_FILES_CNT; i++) { const BmpFileInfo *bmp_info = &bmp_files_list[i]; memset(cmd, 0, sizeof(cmd)); sprintf(cmd, "ext4size mmc 1:0x%x /logo_files/%s", partIndex, bmp_info->bmp_filename); debug("Execute cmd: %s\n", cmd); ret = run_command(cmd, 0); if (ret != 0) { printf("Error: run ext4size for %s failed (ret=%d)\n", bmp_info->bmp_filename, ret); return -1; } file_size = env_get_hex("filesize", 0); if (file_size == 0) { printf("Error: get %s size failed (size=0)\n", bmp_info->bmp_filename); return -1; } printf("%s size: 0x%lx\n", bmp_info->bmp_filename, file_size); memset(cmd, 0, sizeof(cmd)); sprintf(cmd, "ext4load mmc 1:0x%x 0x%lx /logo_files/%s", partIndex, current_load_addr, bmp_info->bmp_filename); debug("Execute cmd: %s\n", cmd); ret = run_command(cmd, 0); if (ret != 0) { printf("Error: run ext4load for %s failed (ret=%d)\n", bmp_info->bmp_filename, ret); return -1; } memset(env_data, 0, sizeof(env_data)); sprintf(env_data, "0x%lx", current_load_addr); snprintf(cmd, sizeof(cmd), "%s_offset", bmp_info->env_prefix); ret = env_set(cmd, env_data); if (ret != 0) { printf("Error: set %s failed\n", cmd); return -1; } printf("Set %s = %s\n", cmd, env_data); memset(env_data, 0, sizeof(env_data)); sprintf(env_data, "0x%lx", file_size); snprintf(cmd, sizeof(cmd), "%s_size", bmp_info->env_prefix); ret = env_set(cmd, env_data); if (ret != 0) { printf("Error: set %s failed\n", cmd); return -1; } printf("Set %s = %s\n", cmd, env_data); current_load_addr += file_size; env_set_hex("filesize", 0); env_set("read_logo_oem", "true"); } printf("All BMP files load success!\n"); return 0; } #endif static int is_target_rebootmode(const char *rebootmode) { const char *targets[] = {"cold_boot", "turn_secure"}; for (size_t i = 0; i < sizeof(targets)/sizeof(targets[0]); i++) { if (rebootmode && !strcmp(rebootmode, targets[i])) { return 1; } } return 0; } static int validate_udisk_files(usb_upgrade_files_t *files, int *has_autoupgrade, int *has_update) { int ret = 0; char cmd[256]; ulong loadaddr = (ulong)env_get_hex("loadaddr", 0x4000); ulong tmp_addr = loadaddr - 0x1000; /* Check aml_autoupgrade */ sprintf(cmd, "fatload usb 0 %lx aml_autoupgrade 1", tmp_addr); if (run_command(cmd, 0) == 0) *has_autoupgrade = 1; else *has_autoupgrade = 0; /* Check aml_sdc_burn.ini */ sprintf(cmd, "fatload usb 0 %lx aml_update 1", tmp_addr); if (run_command(cmd, 0) == 0) *has_update = 1; else *has_update = 0; printf("aml_autoupgrade=%d, aml_update=%d\n", *has_autoupgrade, *has_update); if ((!*has_autoupgrade && !*has_update) || (*has_autoupgrade && *has_update)) { printf("Invalid, skip USB upgrade\n"); return -1; } /* Scan USB for upgrade files */ ret = scan_usb_upgrade_files(files); if (ret != 0) { printf("Failed to scan USB files\n"); return -1; } if (*has_autoupgrade) { if (files->big_pkg_count > 1) { printf("ERROR: Multiple big packages found (%d)\n", files->big_pkg_count); return -1; } if (files->pid_count > 1) { printf("ERROR: Multiple PID files found (%d)\n", files->pid_count); return -1; } if (files->big_pkg_count == 0 && files->small_pkg_count == 0 && files->pid_count == 0) { printf("No upgrade files found\n"); return -1; } } else if (*has_update) { int total_files = files->big_pkg_count + files->small_pkg_count + files->pid_count; printf("=== Single-file upgrade mode (aml_update) ===\n"); printf("Total upgrade files found: %d\n", total_files); if (total_files != 1) { printf("ERROR: Single-file mode requires exactly 1 file, found %d\n", total_files); return -1; } } return 0; } static int do_udisk_check(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = 0; #ifdef CONFIG_REPLACE_TV_MODE usb_upgrade_files_t files; int has_autoupgrade = 0; int has_update = 0; char *rebootmode = env_get("reboot_mode"); char *usb_burn_state = env_get("usb_burn_state"); char *udisk_burn_state = env_get("udisk_burn_state"); env_set("udisk_check", "0"); if (argc > 1) return CMD_RET_USAGE; printf("udisk_burn_state is %s\n", udisk_burn_state ? udisk_burn_state : "NULL"); printf("rebootmode is %s\n", rebootmode ? rebootmode : "NULL"); printf("usb_burn_state is %s\n", usb_burn_state ? usb_burn_state : "NULL"); int is_cold_boot = is_target_rebootmode(rebootmode); int is_after_big_pkg = (usb_burn_state && !strcmp(usb_burn_state, "2")); int is_udisk_state = (udisk_burn_state && !strcmp(udisk_burn_state, "1")); if (is_cold_boot || is_after_big_pkg) { ret = run_command("usb start 0", 0); if (ret != 0) { printf("USB start failed\n"); return CMD_RET_FAILURE; } ret = validate_udisk_files(&files, &has_autoupgrade, &has_update); if (ret != 0) return CMD_RET_FAILURE; printf("U-disk check passed: Valid upgrade files found.\n"); env_set("jpg_logo", "false"); env_set("udisk_check", "true"); if (is_udisk_state && files.pid_count > 0 && files.pid_name[0] != '\0') { printf("found valid pid file %s, update it\n", files.pid_name); int pid_ret = check_pid_update_udisk(files.pid_name, 0); if (pid_ret == 0) { printf("write pid from %s ok\n", files.pid_name); env_set("udisk_burn_state", "0"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p udisk_burn_state;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif run_command("reboot", 0); } else { printf("write pid from %s failed\n", files.pid_name); } } } #endif return CMD_RET_SUCCESS; } static int do_udisk_burn(cmd_tbl_t *cmdtp, int flag, int argc, char * const argv[]) { int ret = 0; int odm_ext_flag = -1; int pid_flag = -1; int logo_update_flag = -1; int oem_flag = -1; if (argc > 1) return CMD_RET_USAGE; #ifdef CONFIG_REPLACE_TV_MODE char *rebootmode = env_get("reboot_mode"); char *usb_burn_state = env_get("usb_burn_state"); char *udiskcheck = env_get("udisk_check"); printf("rebootmode is %s\n", rebootmode ? rebootmode : "NULL"); printf("usb_burn_state is %s\n", usb_burn_state ? usb_burn_state : "NULL"); printf("udiskcheck is %s\n", udiskcheck ? udiskcheck : "NULL"); int is_cold_boot = is_target_rebootmode(rebootmode); int is_after_big_pkg = (usb_burn_state && !strcmp(usb_burn_state, "2")); if ((is_cold_boot || is_after_big_pkg) && udiskcheck && !strcmp(udiskcheck, "true")) { int has_autoupgrade = 0; int has_update = 0; usb_upgrade_files_t files; int i; env_set("usb_burn_state", "0"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p usb_burn_state;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif ret = run_command("usb start 0", 0); if (ret != 0) { printf("USB start failed, skip USB upgrade\n"); return ret; } /* Re-validate files before burning */ ret = validate_udisk_files(&files, &has_autoupgrade, &has_update); if (ret != 0) { printf("Validation failed during burn phase\n"); return ret; } env_set("jpg_logo", "false"); prepare_display(); if (has_autoupgrade) { /* * Multi-file upgrade mode (aml_autoupgrade) * Case 1: big_pkg > 1 -> abort * Case 2: pid > 1 -> abort * Case 3: big_pkg 0-1, pid 0-1, small_pkg 1-3 -> upgrade */ printf("=== Multi-file upgrade mode (aml_autoupgrade) ===\n"); /* Step 1: Upgrade big package (if exists) */ if (files.big_pkg_count == 1 && !is_after_big_pkg) { printf("Upgrading big package: %s\n", files.big_pkg_name); check_udisk_burn_fw(&files); } /* Step 2: Upgrade small packages */ for (i = 0; i < files.small_pkg_count; i++) { char part_name[MAX_FILENAME_LEN]; strncpy(part_name, files.small_pkg_names[i], MAX_FILENAME_LEN - 1); /* Remove .img suffix */ char *suffix = strstr(part_name, ".img"); if (suffix) *suffix = '\0'; printf("Upgrading small package: %s\n", part_name); ret = check_partition_update(part_name); if (ret == 0) { printf("Successfully updated: %s\n", files.small_pkg_names[i]); if (strncmp(part_name, "tvconfig_udisk", 14) == 0) odm_ext_flag = 0; else if (strncmp(part_name, "oem_udisk", 9) == 0 || strncmp(part_name, "skw_udisk", 9) == 0 || strncmp(part_name, "vbmeta_udisk", 12) == 0 || strncmp(part_name, "bootloader_udisk", 16) == 0) logo_update_flag = 0; if (strncmp(part_name, "oem_udisk", 9) == 0 && files.big_pkg_count == 0) oem_flag = 0; } run_command("sleep 3", 0); } /* Step 3: Upgrade PID (if exists) */ if (files.pid_count == 1) { printf("Upgrading PID: %s\n", files.pid_name); pid_flag = check_pid_update_udisk(files.pid_name, 1); run_command("sleep 3", 0); } } else if (has_update) { /* * Single-file upgrade mode (aml_update) * Only upgrade if exactly ONE file from the 10 known files exists */ printf("=== Single-file upgrade mode (aml_update) ===\n"); if (files.big_pkg_count == 1 && !is_after_big_pkg) { printf("Upgrading single big package: %s\n", files.big_pkg_name); check_udisk_burn_fw(&files); } else if (files.small_pkg_count == 1) { char part_name[MAX_FILENAME_LEN]; strncpy(part_name, files.small_pkg_names[0], MAX_FILENAME_LEN - 1); char *suffix = strstr(part_name, ".img"); if (suffix) *suffix = '\0'; printf("Upgrading single small package: %s\n", part_name); ret = check_partition_update(part_name); if (ret == 0) { if (strncmp(part_name, "tvconfig_udisk", 14) == 0) odm_ext_flag = 0; else if (strncmp(part_name, "oem_udisk", 9) == 0 || strncmp(part_name, "skw_udisk", 9) == 0 || strncmp(part_name, "vbmeta_udisk", 12) == 0 || strncmp(part_name, "bootloader_udisk", 16) == 0) logo_update_flag = 0; if (strncmp(part_name, "oem_udisk", 14) == 0) oem_flag = 0; } } else if (files.pid_count == 1) { printf("Upgrading single PID: %s\n", files.pid_name); pid_flag = check_pid_update_udisk(files.pid_name, 1); run_command("sleep 3", 0); } } /* Post-upgrade actions */ printf( "Upgrade results: odm_ext_flag=%d, pid_flag=%d, logo_update_flag=%d\n", odm_ext_flag, pid_flag, logo_update_flag); if (oem_flag == 0) { printf("update oem, set env\n"); env_set("oem_update_flag", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p oem_update_flag;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } if (odm_ext_flag == 0 || pid_flag == 0) { printf("update odm_ext or pid, erase param\n"); run_command("store erase param 0 0", 0); env_set("ini_update_flag", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p ini_update_flag;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif env_set("powermode_ini", "1"); #if CONFIG_IS_ENABLED(AML_UPDATE_ENV) run_command("update_env_part -p powermode_ini;", 0); #else run_command("defenv_reserv; saveenv;", 0); #endif } else if (logo_update_flag == 0) { printf("update logo\n"); run_command("sleep 2", 0); } run_command("reboot", 0); } #endif return ret; } U_BOOT_CMD (udisk_check, 2, 0, do_udisk_check, "check valid update files on udisk", " " ); U_BOOT_CMD (udisk_burn, 2, 0, do_udisk_burn, "perform udisk upgrade", " " );