#! /bin/bash # # Copyright (c) 2021-2022 Amlogic, Inc. All rights reserved. # # SPDX-License-Identifier: MIT # ################################################################################ # Heterogeneous Firmware Compilation and Unified Image Provisioning Framework # # This script orchestrates a multi-stage automated build pipeline specifically # engineered for embedded ecosystems with heterogeneous processor architectures, # notably integrating ARM application cores and specialized Xtensa DSP engines. # The framework enforces a systematic firmware lifecycle methodology, # encompassing dynamic parameter resolution, cross-compilation toolchain # configuration, and cryptographic image signing (via MCUboot or U-Boot # standards). It resolves hardware-specific configuration matrices to guarantee # binary integrity across diverse SoC variants. # The pipeline culminates in the synthesis of a monolithic, deplorable system # image (supporting both OTA and local upgrade formats), consolidating # distributed binary artifacts while preserving diagnostic symbols and # maintaining rigorous workspace sanitization. ################################################################################ set -euo pipefail trap 'clean_up' EXIT SIGINT SIGTERM # Global variable values initialization # Inheritance of Environment Variables from the pkg_env.sh Shell. CHOICE_PACKAGE=${CHOICE_PACKAGE:-0} PACKAGE_COMBINATION=${PACKAGE_COMBINATION:-""} KERNEL=${KERNEL:-""} FW_LISTS=() BOOTLOADER_TYPE=${BOOTLOADER_TYPE:-"uboot"} WAITING_PID=${WAITING_PID:-""} PKG_PRODUCT=${PKG_PRODUCT:-""} PKG_IMG_DIR=${PKG_IMG_DIR:-""} PKG_VERBOSE=${PKG_VERBOSE:-""} PKG_GEN_OTA=${PKG_GEN_OTA:-""} PKG_BUILD_LOG=${PKG_BUILD_LOG:-""} PKG_DBG_EN=${PKG_DBG_EN:-""} PKG_IMG_CFG_FILE=${PKG_IMG_CFG_FILE:-""} PKG_CONSUME=${PKG_CONSUME:-0.00} declare -i RTOS_XIP_RAM_ADDR=0 declare -i RTOS_XIP_ROM_ADDR=$((0)) RTOS_XIP_RAM_ROM_FILL_SIZE=$((8192)) RTOS_OUTPUT_DIR=${RTOS_OUTPUT_DIR:-""} RTOS_ROOT_DIR=${RTOS_ROOT_DIR:-""} RTOS_BUILD_DIR=${RTOS_BUILD_DIR:-""} RTOS_DBG_FILE_PREFIX=${RTOS_DBG_FILE_PREFIX:-""} RTOS_IMG_DIR=${RTOS_IMG_DIR:-""} RTOS_BIN_FILE=${RTOS_BIN_FILE:-""} RTOS_BUILD_CORE=${RTOS_BUILD_CORE:-""} RTOS_FW_TOOL=${RTOS_FW_TOOL:-""} RTOS_BUILD_NINJA=${RTOS_BUILD_NINJA:-""} RTOS_WCN_FW_SRC_DIR=${RTOS_WCN_FW_SRC_DIR:-""} RTOS_BRANCH_TYPES=("hifi_dsp" "speaker") RTOS_USER_RODARA_DIR=${RTOS_BRANCH_TYPES:-""} RTOS_LOAD_ADDR=${RTOS_LOAD_ADDR:-""} RTOS_MKLFS_BLOCK_SZ="4096" RTOS_MKLFS_PAGE_SZ="256" RTOS_MKLFS_READ_SZ="256" RTOS_MKLFS_SRC_PATH=${RTOS_MKLFS_SRC_PATH:-""} RTOS_MKLFS_DST_PATH=${RTOS_MKLFS_DST_PATH:-""} RTOS_SCATTER_LOAD=${RTOS_SCATTER_LOAD:-""} # partition name : partition start address # Constructed using the gen_partition_map routine. declare -A PART_ADDR=() # key(partition name): val(partition start address) declare -A PART_SIZE=() # key(partition name): val(partition size) declare -A SZ_MAP=( [SZ_1K]=$((1024)) [SZ_2K]=$((2*1024)) [SZ_4K]=$((4*1024)) [SZ_8K]=$((8*1024)) [SZ_16K]=$((16*1024)) [SZ_64K]=$((64*1024)) [SZ_128K]=$((128*1024)) [SZ_256K]=$((256*1024)) [SZ_512K]=$((512*1024)) [SZ_1M]=$((1024*1024)) ) log_info() { echo -e "[INFO]: \033[32m$*\033[0m"; } log_note() { echo -e "\033[7m>>> $*\033[0m"; } log_warn() { echo -e "[WARN]: \033[33m$*\033[0m"; } log_err() { echo -e "[ERROR]: \033[31m$*\033[0m"; } log_msg() { echo -en ">>> $*"; } log_dbg() { [[ "$PKG_DBG_EN" == "y" ]] && echo -e "[DBG]: \033[34m$*\033[0m"; } function log_ok() { kill -9 "$WAITING_PID" 2>/dev/null || true wait "$WAITING_PID" 2>/dev/null || true printf "\b\b" echo -e "\e[32m OK(${PKG_CONSUME}S)\e[0m"; } function log_fail() { kill -9 "$WAITING_PID" 2>/dev/null || true wait "$WAITING_PID" 2>/dev/null || true printf "\b\b" echo -e "\e[31m Failed\e[0m" if [[ -f "$PKG_BUILD_LOG" ]]; then cat $PKG_BUILD_LOG fi } up_pid() { WAITING_PID=$!; } function waiting() { local spin_str='|/-\' local tmp_str='' # tput civis while true; do tmp_str=${spin_str#?} printf " %c" "$spin_str" spin_str=$tmp_str${spin_str%"$tmp_str"} sleep 0.1 printf "\b\b" done } function waiting_start() { start=$(date +%s%N) log_msg "$@" waiting & up_pid } function waiting_end() { end=$(date +%s%N) if [[ "$1" == 0 ]]; then PKG_CONSUME=$(bc -l <<< "scale=2; ($end - $start)/1000000000") log_ok else log_fail return 1 fi } # # Write an 8-bit value to a binary file. # # Arguments: # $1 - Target binary file. # $2 - Byte offset in the file. # $3 - 8-bit value to write. function write_od_to_bin_1() { target_file=$1 offset=$2 od_value=$3 size_hex=$(printf '%02x' $od_value) size_little_hex=$(echo -n "$size_hex" | grep -o '..' | tac | tr -d '\n') echo -n $size_little_hex | xxd -r -p | dd of=$target_file bs=1 \ seek=$offset conv=notrunc } # # Write a 32-bit value to a binary file in little-endian format. # # Arguments: # $1 - Target binary file. # $2 - Byte offset in the file. # $3 - 32-bit value to write. function write_od_to_bin_4() { local target_file=$1 local offset=$2 local od_value=$3 local size_hex=$(printf '%08x' $od_value) local size_little_hex=$(echo -n "$size_hex" | grep -o '..' | tac | tr -d '\n') echo -n $size_little_hex | xxd -r -p | dd of=$target_file \ bs=1 seek=$offset conv=notrunc } # # Clean up resources and terminate the current build flow. # function clean_up() { if [[ -n "${WAITING_PID:-}" ]] && kill -0 "$WAITING_PID" 2>/dev/null; then kill -9 "$WAITING_PID" 2>/dev/null || true fi set +euo pipefail return 1 } # # Align a value up to the specified boundary. # # Arguments: # $1 - Value to align. # $2 - Alignment size. align_up() { local -i value=$(( $1 )) local -i align=$(( $2 )) echo $(((value + align - 1) & ~(align - 1))) } # # Parse the selected package target and initialize packaging paths. # # This function extracts architecture, SoC, board, and product # information from PACKAGE_COMBINATION according to CHOICE_PACKAGE, # then prepares the output directory and build log. function parse_pkg_target() { local arch_prefix="" local product_suffix="" #Get the packed sequence selected by the user j=0 while IFS= read -r LINE; do if [[ "$j" -eq "$CHOICE_PACKAGE" ]]; then read -ra PACKAGE_ARRY <<< "$LINE" fi ((j++)) done < "$PACKAGE_COMBINATION" #parameter check if [ -z "$PACKAGE_ARRY" ]; then log_err " Please execute source scripts/pkg_env.sh" return 1 fi #parameter check j=0 unset pkg_arch pkg_soc pkg_board pkg_product pkg_size=${#PACKAGE_ARRY[@]} for ((loop = 0; loop < ${pkg_size}; loop += 4)); do [[ $((loop + 3)) -lt $pkg_size ]] || { log_err "ERROR: malformed PACKAGE_ARRY ($((loop + 3)) > $pkg_size)" return 1 } pkg_arch[j]=${PACKAGE_ARRY[loop]} pkg_soc[j]=${PACKAGE_ARRY[loop + 1]} pkg_board[j]=${PACKAGE_ARRY[loop + 2]} pkg_product[j]=${PACKAGE_ARRY[loop + 3]} arch_prefix="$arch_prefix""${pkg_arch[j]}""-" product_suffix="$product_suffix""-""${pkg_product[j]}" ((j+=1)) done # set the board configuration path PKG_ORIGIN_CFG_DIR="$RTOS_ROOT_DIR/image_packer/${pkg_soc[0]}" # mirror storage path package_dir=$RTOS_ROOT_DIR/output/packages platform="${arch_prefix}""${pkg_soc[0]}"-"${pkg_board[0]}" PKG_IMG_DIR=$package_dir/$platform PKG_BUILD_LOG="$PKG_IMG_DIR/pkb_build.log" # create mirror file directory rm -fr $PKG_IMG_DIR mkdir -p $PKG_IMG_DIR : > "$PKG_BUILD_LOG" } # # Signs and packages the RTOS kernel and Bluetooth firmware using the MCUboot # imgtool utility for secure boot deployment. # # Arguments: # - $1 target SoC architecture name. # - $2 target board name. # - $3 target product variant name. function package_kernel_for_mcuboot() { local m_lib_dir=${RTOS_BUILD_DIR}/lib/mcuboot local m_sig_dir=${m_lib_dir}/scripts local m_sig_tool=${m_sig_dir}/imgtool.py local m_out_dir=${RTOS_BUILD_DIR}/output/$1-$2-mcuboot local m_cfg=$m_out_dir/$KERNEL/.config local img_in_file=$RTOS_OUTPUT_DIR/$KERNEL/$KERNEL.bin local img_out_file=$RTOS_OUTPUT_DIR/images/$KERNEL-signed.bin local m_slot_sz="" local bt_in_file="" local bt_out_file="" local prf_conf="" local bt_slot_sz="" m_slot_sz="$(grep -E \ "^CONFIG_LIB_MCUBOOT_KERNEL_SIZE=" "$m_cfg" | cut -d '=' -f2)" python3 $m_sig_tool sign --key $m_lib_dir/root-rsa-2048.pem \ --header-size 0x1000 --align 4 --slot-size ${m_slot_sz} \ --pad --version 1.0.0 --pad-header --load-addr 0x10000 \ ${img_in_file} \ ${img_out_file} cp ${img_out_file} ${RTOS_IMG_DIR}/rtos-mcuImage prf_conf=${RTOS_BUILD_DIR}/output/$1-$2-$3/$KERNEL/.config bt_in_file=\ ${RTOS_BUILD_DIR}/boards/${pkg_arch[0]}/${pkg_board[0]}/bt_fw.bin bt_out_file=$PKG_IMG_DIR/bt-signed.bin bt_slot_sz="$( grep -E "^CONFIG_LIB_MCUBOOT_BT_SIZE=" "$m_cfg" | cut -d '=' -f2)" python3 $m_sig_tool sign --key $m_lib_dir/root-rsa-2048.pem \ --header-size 0x1000 --align 4 --slot-size ${bt_slot_sz} \ --version 1.0.0 \ --pad-header \ ${bt_in_file} \ ${bt_out_file} cp "$bt_out_file" "${RTOS_IMG_DIR}/bt-signed.bin" } # # Wraps a raw RTOS binary into a U-Boot legacy image format wrapper # as a standalone executable using the 'mkimage' tool. # # Arguments: # - $1 target CPU architecture (e.g., "arm", "arm64"). # - $2 hex memory address for both load and execution entry. # - $3 path to the input raw binary payload file. # - $4 destination path for the generated U-Boot legacy image file. function mkimg_rtos_for_uboot() { local arch=$1 local load_addr=$2 local input=$3 local output=$4 # -A Operating system type # -O Image type: standalone executable image # -C Compression type: no compression # -a Load address in memory # -e Entry point address # -n Image name # -d Input binary file # Output image file mkimage -A ${arch} \ -O u-boot \ -T standalone \ -C none \ -a ${load_addr} \ -e ${load_addr} \ -n rtos \ -d ${input} \ ${output} } # # Generate and source the RTOS .config file. # # Arguments: # - $1 target SoC architecture name. # - $2 target board name. # - $3 target product variant name. # - $4 additional build variation context. function gen_dot_config() { # Every variable from .config should be unset first in here to provent # effects the different products or environment. unset CONFIG_AML_FW_FS unset CONFIG_AML_FW_BUILTIN unset CONFIG_USER_RODATA_FS_IMG unset CONFIG_LFS_USE_BLOCK [[ -f "$RTOS_SCATTER_LOAD" ]] && rm -r "$RTOS_SCATTER_LOAD" set +euo pipefail source scripts/env.sh $1 $2 $3 $4 set -euo pipefail make distclean && make config source $RTOS_OUTPUT_DIR/$KERNEL/.config log_dbg "Completion of generating .config file!" } # # Evaluate a size expression and return a hex value. # # Arguments: # - $1 the size expression to be evaluated (spaces are auto-stripped). function eval_size_expr() { local expr="$1" expr="${expr// /}" local total=0 # Evaluate multi-block expressions containing a coefficient and symbol. if [[ "$expr" =~ ^([0-9]+)\*([A-Z0-9_]+)$ ]]; then local num="${BASH_REMATCH[1]}" local sym="${BASH_REMATCH[2]}" total=$(( num * SZ_MAP[$sym] )) # Evaluate standalone symbolic strings mapped inside the global size map. elif [[ "$expr" =~ ^([A-Z0-9_]+)$ ]]; then total=$(( SZ_MAP[$expr] )) # Evaluate direct hexadecimal literal tokens. elif [[ "$expr" =~ ^0x[0-9a-fA-F]+$ ]]; then total=$(( expr )) # Evaluate direct base-10 decimal integer tokens. elif [[ "$expr" =~ ^[0-9]+$ ]]; then total=$(( expr )) else log_err "unsupported size expr: $expr" >&2 return 1 fi printf "0x%x\n" "$total" } # # Generate partition address and size map from partition_tbl.c. # # Builds PART_ADDR and PART_SIZE by evaluating partition definitions # after C preprocessing. function gen_partition_map() { local compiler="" local cmake_cache="${RTOS_OUTPUT_DIR}/${KERNEL}/CMakeCache.txt" local autoconf="${RTOS_OUTPUT_DIR}/${KERNEL}/autoconf.h" local part_tbl="${RTOS_BUILD_DIR}/boards/${ARCH}/${BOARD}/partition_tbl.c" local part_name="" local size_expr="" local size_hex="" local part_start=$((0x100000)) # Extract the explicit cross-compiler binary filepath from the CMake cache. compiler=$( awk -F= '/^CMAKE_C_COMPILER:FILEPATH/ {print $2; exit}' \ "$cmake_cache" 2>/dev/null ) if [[ -z "$compiler" ]]; then #Compatible with different cmake version compiler=$( awk -F= '/^CMAKE_C_COMPILER:STRING/ {print $2; exit}' \ "$cmake_cache" 2>/dev/null ) fi if [[ -z "$compiler" ]]; then log_err "error: compiler not found" return 1 fi # Preprocess the C layout file to resolve structural definitions and # evaluate line-by-line. while IFS= read -r line; do # Skip preprocessor directives. [[ "$line" =~ ^[[:space:]]*# ]] && continue # Match the name of partition item. if [[ "$line" =~ \.name[[:space:]]*=[[:space:]]*\"([^\"]+)\" ]]; then part_name="${BASH_REMATCH[1]}" continue fi # Match the size of partition item. if [[ "$line" =~ \.size[[:space:]]*=[[:space:]]*(.+) ]]; then size_expr="${BASH_REMATCH[1]}" # Trim off common C-syntax trailing delimiters size_expr="${size_expr%%;*}" size_expr="${size_expr%%}*" size_expr="${size_expr%%,*}" size_expr="${size_expr//[[:space:]]/}" # Bypass dynamically evaluated full-disk markers if [[ "$size_expr" == "MTDPART_SIZ_FULL" ]]; then continue fi if ! size_hex=$(eval_size_expr "$size_expr"); then return 1 fi PART_ADDR["$part_name"]=$(printf "0x%x" "$part_start") PART_SIZE["$part_name"]=$(printf "0x%x" "$size_hex") ((part_start += $((size_hex)))) part_name="" fi done < <("$compiler" -E -include "$autoconf" "$part_tbl" 2>/dev/null) } # # Embed DSP and WCN firmware into a filesystem image. # # This function collects DSP boot images and wireless firmware, # stages them into a temporary filesystem directory, and generates # a LittleFS image using gen_fs_img(). # # Arguments: # $1 - Filesystem directory name. # $2 - Output filesystem image name. # $3 - Filesystem image size. function embed_firmware_as_fs() { local fs_dir_name="$1" local fs_img_name="$2" local fs_img_sz="$3" # Collect DSP firmware. while IFS= read -r -d '' f; do FW_LISTS+=("$f") done < <(find "${PKG_IMG_DIR}" \ -type f -name "dspboot*.bin" -print0) # Collect WCN firmwares. while IFS= read -r -d '' f; do FW_LISTS+=("$f") done < <(find "${RTOS_WCN_FW_SRC_DIR}" -type f \ \( -name "*.bin" -o -name "aml_wifi_rf.txt" \) -print0) # Prepare firmware for generate filesystem image or builtin image. for fw in "${FW_LISTS[@]}"; do install -m 644 "$fw" "$PKG_IMG_DIR/system_fs/" || return 1 done gen_fs_img "$fs_dir_name" "$fs_img_name" "$fs_img_sz" || return 1 return 0 } # # Embed DSP firmware into RTOS rodata as a generated header file. # # This function checks for DSP boot images in PKG_IMG_DIR and, if found, # uses RTOS_FW_TOOL to generate a C header file for firmware embedding. function embed_firmware_as_rodata() { [[ -f ${RTOS_FW_TOOL} ]] || return 1 # Integration of DSP firmware into the rtos rodata if find "$PKG_IMG_DIR" -maxdepth 1 -type f | \ grep -Eq '/dspboot.*\.bin$'; then python3 ${RTOS_FW_TOOL} \ -I ${PKG_IMG_DIR} \ -o ${RTOS_OUTPUT_DIR}/${KERNEL}/dsp_firmware.h fi } # # Select firmware embedding method based on build configuration. # # Depending on configuration, system firmware is either embedded as # a LittleFS image or compiled into RTOS rodata. function embed_firmware() { if [[ "${CONFIG_AML_FW_FS:-n}" == "y" ]]; then embed_firmware_as_fs "system_fs" "system.lfs" "2097152" || return 1 elif [[ "${CONFIG_AML_FW_BUILTIN:-n}" == "y" ]]; then embed_firmware_as_rodata fi log_dbg "Implementation and Integration of the System Firmware" return 0 } # # Recalculate XIP memory layout (RAM/ROM address and size). # # This function rebuilds the RTOS image to obtain final binary sizes, # then computes aligned XIP RAM/ROM addresses and flash region length. # The results are exported for CMake to consume in downstream builds. function auto_get_xip_addr_len() { # Rebuild the workspace from scratch to generate XIP-compliant binaries. [[ -f "${RTOS_BUILD_NINJA}" ]] && rm -f ${RTOS_BUILD_NINJA} if [[ "${CONFIG_BACKTRACE:-n}" == "y" ]]; then make backtrace else make fi [[ "$?" == 0 ]] || return 1 # Extract file sizes of the generated RAM runtime binary and ROM code # segment local -i xip_ram_sz=$(stat -c %s \ "${RTOS_OUTPUT_DIR}/${KERNEL}/${KERNEL}.bin") local -i xip_rom_sz=$(stat -c %s \ "${RTOS_OUTPUT_DIR}/${KERNEL}/freertos_b.bin") if [[ "${PKG_PRODUCT}" != "recovery" ]]; then RTOS_XIP_RAM_ADDR=${PART_ADDR["kernel"]} else RTOS_XIP_RAM_ADDR=${PART_ADDR["recovery"]} fi log_dbg "Before align: xip ram/rom: $xip_ram_sz/$xip_rom_sz" RTOS_XIP_ROM_ADDR=$(( RTOS_XIP_RAM_ADDR + xip_ram_sz + RTOS_XIP_RAM_ROM_FILL_SIZE)) RTOS_XIP_ROM_ADDR=$(align_up ${RTOS_XIP_ROM_ADDR} 4096) if [[ "${CONFIG_USER_RODATA_FS_IMG:-n}" == "y" ]]; then xip_rom_sz=$(align_up ${xip_rom_sz} 0x10000) xip_rom_sz=$((xip_rom_sz + CONFIG_USER_RODATA_SIZE)) fi xip_rom_sz=$(align_up ${xip_rom_sz} 0x10000) log_dbg "After align: xip rom addr/size $RTOS_XIP_ROM_ADDR/$xip_rom_sz" # These two variables are exported exclusively when the XIP_MODE is enabled. # They are subsequently captured by the CMake process to participate in # downstream compilation. FLASH_XIP_START_ADDR=$((RTOS_XIP_ROM_ADDR | $FLASH_XIP_START_ADDR)) FLASH_XIP_LENGTH=$xip_rom_sz printf "\033[1mFLASH_XIP_START_ADDR: 0x%X\033[0m\n" "$FLASH_XIP_START_ADDR" printf "\033[1mFLASH_XIP_LENGTH: 0x%X\033[0m\n" "$FLASH_XIP_LENGTH" } # Generate a LittleFS image from a specified directory. # # This function first checks whether the `mklfs` tool exists. If not, it # builds the tool from source and installs it to the destination path. # Then it invokes `mklfs` to package the specified directory into a # LittleFS image with the configured block size, read size, page size, # and image size. # # Arguments: # $1 - Source directory name under PKG_IMG_DIR to be packed. # $2 - Output filesystem image name. # $3 - Filesystem image size in bytes. function gen_fs_img() { local fs_dir_name=$1 local fs_img_name=$2 local fs_img_sz=$3 local fs_img_dir="${PKG_IMG_DIR}/$fs_dir_name" if [[ ! -e "$RTOS_MKLFS_DST_PATH" ]]; then log_dbg "cp $RTOS_MKLFS_SRC_PATH to $RTOS_MKLFS_DST_PATH" install -m 777 "${RTOS_MKLFS_SRC_PATH}" "$RTOS_MKLFS_DST_PATH" fi pushd $PKG_IMG_DIR >/dev/null 2>&1 trap 'trap - RETURN; popd >/dev/null 2>&1' RETURN if [[ "${CONFIG_LFS_USE_BLOCK:-n}" == "y" ]]; then RTOS_MKLFS_BLOCK_SZ="65536" fi ./mklfs -c "$fs_dir_name" \ -b $RTOS_MKLFS_BLOCK_SZ \ -r $RTOS_MKLFS_READ_SZ \ -p $RTOS_MKLFS_PAGE_SZ \ -s "$fs_img_sz" \ -i "$fs_img_name" || return 1 return 0 } # # Generate user_rodata LittleFS image and append it to the RTOS kernel image. # # This function creates a temporary directory under PKG_IMG_DIR, copies # all files from CONFIG_USER_RODATA_PATH into it, and generates a # LittleFS image by calling gen_fs_img(). # # After the filesystem image is generated, the function pads the kernel # image with 0xFF so that its size reaches the end of the kernel # partition, then appends the filesystem image to the kernel image. # # Memory layout: # +---------------------------------------------------+ # | rtos-xip (RTOS_XIP_ROM_ADDR) | # +---------------------------------------------------+ # | Padding area filled with 0xFF | # +---------------------------------------------------+ # | user_rodata LittleFS image | # +---------------------------------------------------+ # # Arguments: # $1 - Temporary directory name used to generate the filesystem image. # $2 - Output LittleFS image name. # $3 - Kernel image file path to which the filesystem image is appended. function gen_user_rodata_fs_img() { local fs_dir_name=$1 local fs_img_name=$2 local kernel_file=$3 local -i kernel_file_sz=0 local -i fill_sz=0 local -i fs_img_sz=0 local fs_img_path="$PKG_IMG_DIR/$fs_img_name" local fs_dir_path="$PKG_IMG_DIR/$fs_dir_name" [[ -e "$fs_dir_path" ]] && rm -rf "$fs_dir_path" mkdir $fs_dir_path if [[ -n "${CONFIG_USER_RODATA_PATH:-""}" ]]; then find $CONFIG_USER_RODATA_PATH/ -type f -exec cp {} $fs_dir_path/ \; \ || return 1 fi gen_fs_img "$fs_dir_name" "$fs_img_name" \ $(printf "%d" "$CONFIG_USER_RODATA_SIZE") || return 1 return 0 # # This code snippet is designed to integrate the user_rodata.lfs into the # rtos-xip. However, a more elegant approach involes allocating the # user_rodata.lfs into the user partition within U-Boot and mapping it to # the user_rodata partition within RTOS. This decoupling facilitates fexibly # customization of the user_rodata partition size to meet diverse customer # requirements. if [[ -f "$kernel_file" ]]; then kernel_file_sz=$(stat -c %s "$kernel_file") else log_err "Error: File $kernel_file does not exist!" return 1 fi if [[ -f "$fs_img_path" ]]; then fs_img_sz=$(stat -c %s "$fs_img_path") else log_err "Error: File $fs_img_path does not exist!" return 1 fi log_dbg "kernel partition size: ${PART_SIZE['kernel']}" local kernel_part_sz=$(echo $((PART_SIZE["kernel"]))) local -i kernel_offset=$((RTOS_XIP_ROM_ADDR - RTOS_XIP_RAM_ADDR)) # fill_sz + kernel_file_sz = PART_SIZE['kernel']} fill_sz=$(( kernel_part_sz - RTOS_XIP_ROM_ADDR + RTOS_XIP_RAM_ADDR - kernel_file_sz)) if [[ "$fill_sz" -gt 0 ]]; then dd if=/dev/zero bs="$fill_sz" count=1 2>/dev/null | \ tr '\000' '\377' >> "$kernel_file" elif [[ "$fill_sz" -lt 0 ]]; then log_err "The size is overflow!" fi log_dbg "kernel_file_sz + fill_sz: $kernel_file_sz + $fill_sz" cat "$fs_img_path" >> "$kernel_file" || return 1 return 0 } # # Prepare an RTOS image for encryption. # # This function creates a U-Boot compatible RTOS image, prepends a # 4032-byte reserved header area, and fills RTOS metadata into the # image header. # # When XIP mode is enabled, additional XIP information, including the # partition name, XIP offset, and XIP size, is written into the header. # # Header layout: # Offset Size Description # ------ ---- ----------------------------- # 1 1 XIP enable flag # 3 4 RTOS image type/version # 7 4 RTOS image size # 16 16 Partition name ("boot" or "recovery") # 32 4 XIP offset # 36 4 XIP image size # 144 16 Partition name ("user") # 160 4 RODATA offset # 164 4 RODATA size # # Arguments: # $1 - Source RTOS image file. # $2 - Output image file prepared for encryption. function pack_rtos_for_encrypt() { local src_file=$1 local dst_file=$2 local part_name="" local -i src_file_sz=0 local -i xip_offset=$((RTOS_XIP_ROM_ADDR - RTOS_XIP_RAM_ADDR)) local -i xip_size=$(stat -c %s ${RTOS_IMG_DIR}/rtos-xip) mkimg_rtos_for_uboot ${ARCH} ${RTOS_LOAD_ADDR} ${RTOS_BIN_FILE} \ ${RTOS_IMG_DIR}/rtos-uImage #preappend 4032B at the beginning and generate image header again dd if=/dev/zero bs=4032 count=1 2>/dev/null | \ cat - "$src_file" > "$dst_file" # Fill the rtos information to header. write_od_to_bin_4 "$dst_file" 3 80 src_file_sz=$(stat -c %s "$src_file") src_file_sz=$((src_file_sz + 4032 - 16)) write_od_to_bin_4 "$dst_file" 7 $src_file_sz if [ "${CONFIG_XIP_MODE:-n}" = "y" ]; then if [[ "${PKG_PRODUCT}" == "recovery" ]]; then part_name="recovery" else part_name="boot" fi write_od_to_bin_1 "$dst_file" 1 0x1 echo -n $part_name | head -c 15 | \ dd of="$dst_file" bs=1 seek=16 conv=notrunc write_od_to_bin_4 "$dst_file" 32 $xip_offset write_od_to_bin_4 "$dst_file" 36 $xip_size fi if [ "${CONFIG_USER_RODATA_FS_IMG:-n}" = "y" ]; then part_name="user" write_od_to_bin_1 "$dst_file" 1 0x1 echo -n $part_name | head -c 15 | \ dd of="$dst_file" bs=1 seek=144 conv=notrunc write_od_to_bin_4 "$dst_file" 160 0 write_od_to_bin_4 "$dst_file" 164 $CONFIG_USER_RODATA_SIZE fi log_dbg "xip_size + xip_offset = $xip_size + $xip_offset" } # # Compile and package the RTOS image for ARM platforms. # # This function performs the complete RTOS build workflow, including: # Supported features: # - XIP (Execute In Place) mode # - user_rodata LittleFS image # - U-Boot image packaging # - MCUboot image packaging # # Arguments: # $1 - Architecture name. # $2 - Board name. # $3 - Product name. # $4 - Package type (e.g. normal, recovery). function _compile_rtos_for_arm() { # target file path RTOS_OUTPUT_DIR=${RTOS_BUILD_DIR}/output/$1-$3-$4 RTOS_IMG_DIR=${RTOS_OUTPUT_DIR}/images RTOS_BIN_FILE=${RTOS_IMG_DIR}/${KERNEL}-signed.bin RTOS_DBG_FILE_PREFIX=${RTOS_OUTPUT_DIR}/${KERNEL}/${KERNEL} RTOS_BUILD_NINJA="${RTOS_OUTPUT_DIR}/${KERNEL}/build.ninja" RTOS_FW_TOOL=${RTOS_BUILD_DIR}/scripts/gen_firmware.py RTOS_MKLFS_SRC_PATH="${RTOS_OUTPUT_DIR}/${KERNEL}/mklfs" RTOS_MKLFS_DST_PATH="${PKG_IMG_DIR}/mklfs" RTOS_WCN_FW_SRC_DIR="${RTOS_BUILD_DIR}/wcn/firmware" RTOS_SCATTER_LOAD="${RTOS_BUILD_DIR}/output/scatter_load.mk" PKG_PRODUCT=$4 pushd $RTOS_BUILD_DIR >/dev/null 2>&1 || return 1 trap 'trap - RETURN; popd >/dev/null 2>&1' RETURN gen_dot_config $1 $2 $3 $4 # Package auxiliary firmware components unless compiling for recovery mode. if [[ "${PKG_PRODUCT}" != "recovery" ]]; then embed_firmware if [[ "$?" != 0 ]]; then log_err "❌ Generate firmware failed!" return 1 fi fi gen_partition_map || return 1 # Handle pre-compilation alignment and layout parameters if XIP is enabled. if [ "${CONFIG_XIP_MODE:-n}" = "y" ]; then export FLASH_XIP_START_ADDR=0xE0000000 export FLASH_XIP_LENGTH=0x500000 auto_get_xip_addr_len || return 1 fi # start compile flow [[ -f "${RTOS_BUILD_NINJA}" ]] && rm -f ${RTOS_BUILD_NINJA} if [[ "${CONFIG_BACKTRACE:-n}" == "y" ]]; then make backtrace else make fi if [ $? -ne 0 ]; then log_err "❌ build rtos image faile error:$?" clean_up return 1 fi # If in XIP MODE, the file is xip_ram file local kernel_bin=${RTOS_BIN_FILE} install -m 644 ${RTOS_OUTPUT_DIR}/${KERNEL}/${KERNEL}.bin ${RTOS_BIN_FILE} # The file is xip_rom file if [ "${CONFIG_XIP_MODE:-n}" = "y" ]; then install -m 644 ${RTOS_OUTPUT_DIR}/${KERNEL}/freertos_b.bin \ ${RTOS_IMG_DIR}/rtos-xip kernel_bin=${RTOS_IMG_DIR}/rtos-xip fi if [[ "${CONFIG_USER_RODATA_FS_IMG:-n}" == "y" ]]; then gen_user_rodata_fs_img "user_rodata_fs" "user_rodata.lfs" $kernel_bin \ || return 1 fi # rtos load address compiler=$RTOS_BUILD_DIR/output/toolchains/$COMPILER-$TOOLCHAIN_KEYWORD NM=$compiler/bin/$TOOLCHAIN_KEYWORD-nm RTOS_LOAD_ADDR=$( $NM -g --defined-only $RTOS_OUTPUT_DIR/$KERNEL/$KERNEL.elf | grep image_start | awk '{print $1}' | sed 's/^0*//' ) if [[ ! -n "$RTOS_LOAD_ADDR" ]]; then log_err "load addr not found" return 1 fi # Package RTOS kernel for uboot. if [[ "$BOOTLOADER_TYPE" == "uboot" ]]; then pack_rtos_for_encrypt \ "${RTOS_IMG_DIR}/rtos-uImage" "${RTOS_IMG_DIR}/rtos-uImage.tmp" mkimg_rtos_for_uboot ${ARCH} ${RTOS_LOAD_ADDR} \ "${RTOS_IMG_DIR}/rtos-uImage.tmp" "${RTOS_IMG_DIR}/rtos-uImage" # Append the XIP ROM to the end of XIP RAM(rtos-uImage). if [ "${CONFIG_XIP_MODE:-n}" = "y" ]; then local rtos_uimg_size=$(stat -c %s ${RTOS_IMG_DIR}/rtos-uImage) local fill_size=$(( RTOS_XIP_ROM_ADDR - RTOS_XIP_RAM_ADDR - rtos_uimg_size)) # Fill up to RTOS_XIP_ROM_ADDR - RTOS_XIP_RAM_ADDR dd if=/dev/zero bs=${fill_size} count=1 >> \ "${RTOS_IMG_DIR}/rtos-uImage" cat ${RTOS_IMG_DIR}/rtos-xip >> ${RTOS_IMG_DIR}/rtos-uImage fi # Copy rtos img to specify directory if [[ "${PKG_PRODUCT}" == "recovery" ]]; then install -m 644 ${RTOS_IMG_DIR}/rtos-uImage \ $PKG_IMG_DIR/rtos.${PKG_PRODUCT} else install -m 644 ${RTOS_IMG_DIR}/rtos-uImage \ $PKG_IMG_DIR/rtos-uImage fi # Package the RTOS kernel for mcuboot. elif [ "$BOOTLOADER_TYPE" == "mcuboot" ]; then package_kernel_for_mcuboot $1 $3 $4 test -f ${RTOS_IMG_DIR}/rtos-mcuImage &&\ cp ${RTOS_IMG_DIR}/rtos-mcuImage $PKG_IMG_DIR/rtos-mcuImage else log_err "Invalid bootloader type: $BOOTLOADER_TYPE" return 1 fi test -f ${RTOS_DBG_FILE_PREFIX}.lst && \ cp ${RTOS_DBG_FILE_PREFIX}.lst $PKG_IMG_DIR/$1-$3.lst test -f ${RTOS_DBG_FILE_PREFIX}.map && \ cp ${RTOS_DBG_FILE_PREFIX}.map $PKG_IMG_DIR/$1-$3.map return 0 } # # Compile and package the RTOS image for Xtensa platforms. # # This function performs the complete RTOS build workflow for Xtensa # targets, including environment initialization, firmware compilation, # DSP boot variant detection, and exporting final images and debug # artifacts. # # Arguments: # $1 - Architecture name. # $2 - Board name. # $3 - Product name. # $4 - Package type. function _compile_rtos_for_xtensa() { # target file path RTOS_OUTPUT_DIR=${RTOS_BUILD_DIR}/output/$1-$3-$4 RTOS_IMG_DIR=${RTOS_OUTPUT_DIR}/images RTOS_BIN_FILE=${RTOS_IMG_DIR}/${KERNEL}-signed.bin RTOS_DBG_FILE_PREFIX=${RTOS_OUTPUT_DIR}/${KERNEL}/${KERNEL} # start compile flow pushd "$RTOS_BUILD_DIR" >/dev/null 2>&1 set +euo pipefail source scripts/env.sh $1 $2 $3 $4 set -euo pipefail make distclean if [[ "${CONFIG_BACKTRACE:-n}" == "y" ]]; then make backtrace else make fi [[ "$?" == 0 ]] || return 1 # Extract the DSP boot variant suffix (a/b) for HiFi4/HiFi5 from the third # argument. The pattern matches strings like "hifi4a", "hifi4b", "hifi5a", # or "hifi5b", and captures only the trailing letter to determine the DSP # boot type. local dspboot_type=$(echo "$3" | grep -oP 'hifi[4-5]\K[a-bA-B]' | tr '[:lower:]' '[:upper:]') test -f ${RTOS_BIN_FILE} && \ cp ${RTOS_BIN_FILE} $PKG_IMG_DIR/dspboot${dspboot_type}.bin test -f ${RTOS_DBG_FILE_PREFIX}.lst && \ cp ${RTOS_DBG_FILE_PREFIX}.lst $PKG_IMG_DIR/$1-$3.lst test -f ${RTOS_DBG_FILE_PREFIX}.map && \ cp ${RTOS_DBG_FILE_PREFIX}.map $PKG_IMG_DIR/$1-$3.map popd >/dev/null 2>&1 return 0 } # # Compile RTOS for ARM targets with optional log redirection and # progress indication. # # Arguments: # $1 - Architecture name. # $2 - Board name. # $3 - Product name. # $4 - Package type. function compile_rtos_for_arm() { if [[ "$PKG_VERBOSE" == "y" ]]; then _compile_rtos_for_arm ${1} ${2} ${3} ${4} || return 1 else waiting_start "Building ${1} ${2} ${3} ${4} ..." _compile_rtos_for_arm ${1} ${2} ${3} ${4} >> "$PKG_BUILD_LOG" 2>&1 waiting_end $? || return 1 fi } # # Compile RTOS for Xtensa targets with optional log redirection and # progress indication. # # This is a wrapper around _compile_rtos_for_xtensa() that controls # console output and build log handling based on PKG_VERBOSE. # # Arguments: # $1 - Architecture name. # $2 - Board name. # $3 - Product name. # $4 - Package type. function compile_rtos_for_xtensa() { if [[ "$PKG_VERBOSE" == "y" ]]; then _compile_rtos_for_xtensa ${1} ${2} ${3} ${4} || return 1 else waiting_start "Building ${1} ${2} ${3} ${4} ..." _compile_rtos_for_xtensa ${1} ${2} ${3} ${4} >> "$PKG_BUILD_LOG" 2>&1 waiting_end $? || return 1 fi } # # Build RTOS images for all configured targets. # # Iterate over all configured targets and invoke the corresponding # build function according to the architecture and RTOS_BUILD_CORE. function build_rtos() { for ((loop = 0; loop < ${#pkg_board[@]}; loop += 1)); do if [[ ${pkg_arch[loop]} == *"arm"* ]] \ && [[ "$RTOS_BUILD_CORE" == "arm" ]]; then compile_rtos_for_arm ${pkg_arch[loop]} ${pkg_soc[loop]} \ ${pkg_board[loop]} ${pkg_product[loop]} || return 1 elif [[ ${pkg_arch[loop]} == "xtensa" ]] \ && [[ "$RTOS_BUILD_CORE" == "xtensa" ]]; then compile_rtos_for_xtensa ${pkg_arch[loop]} ${pkg_soc[loop]} \ ${pkg_board[loop]} ${pkg_product[loop]} || return 1 fi done return 0 } # # Generate package images with optional log redirection and progress # indication. # # This is a wrapper around _gen_pkg_img() that controls console output # and build log handling based on PKG_VERBOSE. function gen_pkg_img() { if [[ "$PKG_VERBOSE" == "y" ]]; then _gen_pkg_img || return 1 else waiting_start "Packing image file ..." _gen_pkg_img >> "$PKG_BUILD_LOG" 2>&1 waiting_end $? || return 1 fi } # # Generate the upgrade package configuration file from the template. # # Entries under #[CUSTOM_OPTION] are included only if the referenced # image exists in PKG_IMG_DIR, while entries under #[CUSTOM_COMMON] # are always preserved. function gen_pkg_cfg() { local pkg_original_cfg=$PKG_ORIGIN_CFG_DIR/aml_upgrade_package.conf PKG_IMG_CFG_FILE="$PKG_IMG_DIR/aml_upgrade_package.conf" custom_state="" while IFS= read -r line; do if [[ "$line" == "[LIST_NORMAL]" || "$line" == "[LIST_VERIFY]" ]]; then echo "$line" >> $PKG_IMG_CFG_FILE continue fi if [[ "$line" == "#[CUSTOM_OPTION]" ]]; then custom_state="OPTION" continue elif [[ "$line" == "#[CUSTOM_COMMON]" ]]; then custom_state="COMMON" continue fi if [[ "$custom_state" == "OPTION" ]]; then local Q='"' file=${line#*file=$Q} file=${file%%$Q*} if [[ -e "$PKG_IMG_DIR/$file" ]]; then echo "$line" >> $PKG_IMG_CFG_FILE continue fi elif [[ "$custom_state" == "COMMON" ]]; then echo "$line" >> $PKG_IMG_CFG_FILE continue fi done < $pkg_original_cfg } function build_bootloader() { if [[ "$PKG_VERBOSE" == "y" ]]; then _build_bootloader || return 1 else waiting_start "Building boorloader($BOOTLOADER_TYPE) ..." _build_bootloader >> "$PKG_BUILD_LOG" 2>&1 waiting_end $? || return 1 fi } # Compile the bootloader function _build_bootloader() { log_info "Building $BOOTLOADER_TYPE ..." if [ "$BOOTLOADER_TYPE" == "mcuboot" ]; then source $RTOS_BUILD_DIR/scripts/package_mcuboot.sh compile_mcuboot \ ${pkg_arch[0]} \ ${pkg_soc[0]} \ ${pkg_board[0]} \ mcuboot \ $PKG_IMG_DIR log_info "Compilation of MCUBoot is successful" else #Select the compile parameters of the bootstrap case ${pkg_board[0]} in 'ad401_a113l') uboot_type="a1_ad401_nand_rtos" ;; 'ad403_a113l') uboot_type="a1_ad40as_nor_rtos" ;; 'ad40as_a113l') uboot_type="a1_ad40as_nor_rtos" ;; 'ad401_a113l_hifi4a_psram') uboot_type="a1_ad401_nor_rtos" ;; 'ad403_a113l_hifi4a') uboot_type="a1_ad403_nand_rtos" ;; *) ;; esac if [ -z "$uboot_type" ]; then log_warn "Waring: Select board(${pkg_board[0]}) not support uboot" return 1 else pushd $RTOS_ROOT_DIR/boot/u-boot trap 'trap - RETURN; popd >/dev/null 2>&1' RETURN ./mk $uboot_type || return 1 test -f build/u-boot.bin && \ cp -av build/u-boot.bin* $PKG_IMG_DIR return 0 fi fi } function gen_ota_img () { if [[ "$PKG_VERBOSE" == "y" ]]; then _gen_ota_img $PKG_IMG_DIR/aml_upgrade_package.img \ >> "$PKG_BUILD_LOG" 2>&1 || return 1 else waiting_start "Packing OTA image ..." _gen_ota_img $PKG_IMG_DIR/aml_upgrade_package.img \ >> "$PKG_BUILD_LOG" 2>&1 waiting_end $? || return 1 fi } # # Generate an OTA package image from the specified upgrade package. # # This function unpacks the upgrade image, extracts supported # partitions, and repackages them into an OTA image. # # Arguments: # $1 - Upgrade package image. function _gen_ota_img() { local image_package=$1 if [[ ! -e "$image_package" ]]; then log_err "❌ $image_package does not exist!" return 1 fi local ota_tool=$RTOS_BUILD_DIR/scripts/ota_img_packer.py local unpackage_dir=$PKG_IMG_DIR/unpackage mkdir -p $unpackage_dir $RTOS_ROOT_DIR/image_packer/aml_image_v2_packer \ -d $image_package $unpackage_dir >/dev/null local ota_cmd_opt="" local ota_partition="" for f in $unpackage_dir/*; do [ -f "$f" ] || continue [[ "$f" =~ \.PARTITION$ ]] || continue ota_partition=${f%.*} ota_partition=${ota_partition##*/} case $ota_partition in # These partitions require a change to their respective names. boot) ota_partition="kernel" ;; user) ota_partition="user_rodata" ;; # These partitions do not support OTA. bootloader | recovery) continue ;; # The remaining partitions are integrated into OTA package under # their respective names. *) ;; esac ota_cmd_opt+=" -f $f -p $ota_partition" done ota_cmd_opt+=" -t ${PKG_IMG_DIR} -o ota_package_img -d" python3 $RTOS_BUILD_DIR/scripts/ota_img_packer.py $ota_cmd_opt } function output_info() { log_info "Package Conf: " echo -e "\t$PKG_IMG_CFG_FILE" log_info "Package Image: " echo -e "\t$PKG_IMG_DIR/aml_upgrade_package.img" if [[ "$PKG_GEN_OTA" == "y" ]]; then log_info "OTA Image: " echo -e "\t$PKG_IMG_DIR/ota_package_img" fi [[ "$PKG_VERBOSE" != "y" ]] && log_info "Build Log: " && \ echo -e "\t$PKG_IMG_DIR/pkg_build.log" } # # Generate the AML upgrade package image. # # This function copies the required configuration files, invokes # aml_image_v2_packer to generate aml_upgrade_package.img, and # removes temporary files after packaging. function _gen_pkg_img() { install $PKG_ORIGIN_CFG_DIR/platform.conf $PKG_IMG_DIR/ install $PKG_ORIGIN_CFG_DIR/usb_flow.aml $PKG_IMG_DIR/ install $PKG_ORIGIN_CFG_DIR/aml_sdc_burn.ini $PKG_IMG_DIR/ $RTOS_ROOT_DIR/image_packer/aml_image_v2_packer \ -r $PKG_IMG_CFG_FILE \ $PKG_IMG_DIR \ $PKG_IMG_DIR/aml_upgrade_package.img if [[ "$?" == 0 ]]; then log_info "Aml Package Successful!" else log_err "Aml Package Failed!" fi cd $PKG_IMG_DIR >/dev/null 2>&1 && \ rm $(ls | grep -v ".lst" | grep -v ".map" | grep -v ".img" | grep -v ".log" | grep -v ".conf") >/dev/null 2>&1 cd - >/dev/null 2>&1 } function parse_param() { while getopts "b:vud" opt; do case "$opt" in b) BOOTLOADER_TYPE="$OPTARG" ;; v) PKG_VERBOSE="y" ;; u) PKG_GEN_OTA="y" ;; d) PKG_DBG_EN="y" ;; ?) ;; esac done } function main() { parse_param $@ parse_pkg_target || return 1 for branch in ${RTOS_BRANCH_TYPES[@]}; do arch_dir=$RTOS_ROOT_DIR/rtos/$branch/arch export RTOS_BUILD_DIR=$RTOS_ROOT_DIR/rtos/$branch if [[ -d "$arch_dir/arm" || -d "$arch_dir/arm64" ]]; then RTOS_BUILD_CORE="arm" elif [ -d $arch_dir/xtensa ]; then RTOS_BUILD_CORE="xtensa" fi build_rtos if [[ $? -ne 0 ]]; then log_err "Building RTOS failed!" return 1 fi done build_bootloader || return 1 gen_pkg_cfg || return 1 gen_pkg_img || return 1 if [[ "$PKG_GEN_OTA" == "y" ]]; then gen_ota_img || return 1 fi output_info } main "$@" set +euo pipefail