Files

1949 lines
54 KiB
C

// SPDX-License-Identifier: (GPL-2.0-only OR MIT)
/*
* Copyright (C) 2025 Amlogic, Inc. All rights reserved
*/
#include <common.h>
#include <command.h>
#include <malloc.h>
#include <linux/ctype.h>
#include <linux/string.h>
#include <amlogic/keyunify.h>
#include <amlogic/aml_efuse.h>
#include <amlogic/storage.h>
#include <asm/amlogic/arch/efuse.h>
#include <amlogic/emmc_partitions.h>
#include <amlogic/store_wrapper.h>
#include <../../drivers/usb/gadget/v2_burning/v2_common/optimus_download.h>
#include <../../drivers/usb/gadget/v2_burning/v2_burning_i.h>
#include <u-boot/sha256.h>
#include <fs.h>
#ifdef CONFIG_DDR_FASTBOOT
#include <amlogic/aml_mmc.h>
#endif
#include <part.h>
#if defined(CONFIG_AML_ANTIROLLBACK) || defined(CONFIG_AML_AVB2_ANTIROLLBACK)
#include <amlogic/anti-rollback.h>
#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",
" "
);