#!/bin/bash # XOS Partition Configuration Update Script # Usage: ./tools/partition/update-partition/update-partition.sh # Description: Updates partition configuration files based on project-specific config set -e # Color output for better readability RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' NC='\033[0m' # No Color # Print colored output print_info() { echo -e "${GREEN}[INFO]${NC} $1" } print_warning() { echo -e "${YELLOW}[WARNING]${NC} $1" } print_error() { echo -e "${RED}[ERROR]${NC} $1" } # Check if jq is available for JSON parsing check_dependencies() { if ! command -v jq &> /dev/null; then print_error "jq is required but not installed. Please install jq to parse JSON configuration." print_info "Ubuntu/Debian: sudo apt-get install jq" print_info "CentOS/RHEL: sudo yum install jq" exit 1 fi } # Parse command line arguments if [ $# -ne 1 ]; then print_error "Usage: $0 " print_info "Example: $0 esl2" exit 1 fi PROJECT_NAME="$1" ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../.." && pwd)" CONFIG_FILE="$ROOT_DIR/product/$PROJECT_NAME/partition-config.json" print_info "Starting partition configuration update for project: $PROJECT_NAME" print_info "Root directory: $ROOT_DIR" # Verify configuration file exists if [ ! -f "$CONFIG_FILE" ]; then print_error "Configuration file not found: $CONFIG_FILE" print_info "Please create the configuration file first." exit 1 fi print_info "Found configuration file: $CONFIG_FILE" # Parse configuration using jq parse_config() { local key="$1" jq -r ".$key // empty" "$CONFIG_FILE" } # Read configuration values PROJECT_NAME_CONFIG=$(parse_config "project_name") CONFIG_NAME=$(parse_config "config_name") PLATFORM=$(parse_config "platform") MEDIUM_TYPE=$(parse_config "medium_type") MEDIUM_SIZE=$(parse_config "medium_size") OS_TYPE=$(parse_config "os_type") # Validate required fields validate_config() { local errors=0 if [ -z "$PROJECT_NAME_CONFIG" ]; then print_error "Missing required field: project_name" ((errors++)) fi if [ -z "$CONFIG_NAME" ]; then print_error "Missing required field: config_name" ((errors++)) fi if [ -z "$PLATFORM" ]; then print_error "Missing required field: platform" ((errors++)) elif [[ ! "$PLATFORM" =~ ^(qm10xd|qm10xv|qm10xh)$ ]]; then print_error "Invalid platform: $PLATFORM (must be qm10xd, qm10xv, or qm10xh)" ((errors++)) fi if [ -z "$MEDIUM_TYPE" ]; then print_error "Missing required field: medium_type" ((errors++)) elif [[ ! "$MEDIUM_TYPE" =~ ^(los_spi-nand|los_spi-nor|los_emmc)$ ]]; then print_error "Invalid medium_type: $MEDIUM_TYPE (must be los_spi-nand, los_spi-nor, or los_emmc)" ((errors++)) fi if [ -z "$OS_TYPE" ]; then print_error "Missing required field: os_type" ((errors++)) elif [[ ! "$OS_TYPE" =~ ^(linux|rtt|linuxrttv2)$ ]]; then print_error "Invalid os_type: $OS_TYPE (must be linux or rtt)" ((errors++)) fi if [ $errors -gt 0 ]; then print_error "Configuration validation failed with $errors errors" exit 1 fi } # Convert size based on filesystem type convert_partition_size() { local size="$1" local fs_type="$2" # Extract numeric value and unit local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') # Convert to bytes for ubi, jffs2, squashfs case "$fs_type" in "ubi"|"jffs2"|"squashfs") case "$unit" in "MB"|"M") echo $((${numeric%.*} * 1024 * 1024)) ;; "KB"|"K") echo $((${numeric%.*} * 1024)) ;; "GB"|"G") echo $((${numeric%.*} * 1024 * 1024 * 1024)) ;; *) # If no unit, assume it's already in bytes echo "$numeric" ;; esac ;; "ext4") # Keep original format for ext4 case "$unit" in "MB"|"M") echo "${numeric%.*}" ;; "KB"|"K") echo $((${numeric%.*} / 1024)) ;; "GB"|"G") echo $((${numeric%.*} * 1024)) ;; *) echo "$numeric" ;; esac ;; "raw"|*) # For raw partitions, keep original format in MB case "$unit" in "MB"|"M") echo "${numeric%.*}" ;; "KB"|"K") echo $((${numeric%.*} / 1024)) ;; "GB"|"G") echo $((${numeric%.*} * 1024)) ;; *) echo "$numeric" ;; esac ;; esac } # Generate partition-info.txt content generate_partition_info() { local partition_info_file="$ROOT_DIR/product/$PROJECT_NAME/partition-info.txt" print_info "Generating partition-info.txt: $partition_info_file" # Create product directory if it doesn't exist mkdir -p "$(dirname "$partition_info_file")" # Check if partition-info.txt already exists if [ -f "$partition_info_file" ]; then # Backup original file and preserve permissions cp -p "$partition_info_file" "${partition_info_file}.backup" # Store original file permissions local original_perms=$(stat -c "%a" "$partition_info_file" 2>/dev/null || echo "644") # Create temporary file for processing local temp_file="${partition_info_file}.tmp" > "$temp_file" # Process existing file line by line while IFS=',' read -r part_name part_fs part_size || [ -n "$part_name" ]; do # Strip carriage returns from all fields part_name=$(echo "$part_name" | tr -d '\r') part_fs=$(echo "$part_fs" | tr -d '\r') part_size=$(echo "$part_size" | tr -d '\r') # Skip empty lines and terminator if [ -z "$part_name" ] || [ "$part_name" = "::" ]; then if [ "$part_name" = "::" ]; then echo "::" >> "$temp_file" fi continue fi # Check if this partition exists in JSON config local json_partition=$(jq -c --arg name "$part_name" '.partition_table[] | select(.name == $name)' "$CONFIG_FILE") if [ -n "$json_partition" ]; then # Partition exists in JSON, process it local size=$(echo "$json_partition" | jq -r '.size') local fs_type=$(echo "$json_partition" | jq -r '.filesystem_type // "raw"') # Only update if filesystem type matches target types (ubi, jffs2, squashfs, ext4) if [[ "$fs_type" =~ ^(ubi|jffs2|squashfs|ext4)$ ]]; then # Convert size based on filesystem type local converted_size="" case "$fs_type" in "ubi"|"jffs2"|"squashfs") # Convert to bytes converted_size=$(convert_partition_size "$size" "$fs_type") ;; "ext4") # Keep MB format for ext4 local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') case "$unit" in "MB"|"M") converted_size="${numeric%.*}" ;; "KB"|"K") converted_size=$((${numeric%.*} / 1024)) ;; "GB"|"G") converted_size=$((${numeric%.*} * 1024)) ;; *) converted_size="$numeric" ;; esac ;; esac echo "${part_name},${fs_type},${converted_size}" | tr -d '\r' >> "$temp_file" else # Keep original line for non-target filesystem types echo "${part_name},${part_fs},${part_size}" | tr -d '\r' >> "$temp_file" fi else # Partition not found in JSON, keep original line echo "${part_name},${part_fs},${part_size}" | tr -d '\r' >> "$temp_file" fi done < "$partition_info_file" # Replace original with updated file and preserve permissions chmod "$original_perms" "$temp_file" 2>/dev/null || true mv "$temp_file" "$partition_info_file" else # File doesn't exist, create new one with all partitions > "$partition_info_file" # Process each partition from JSON while IFS= read -r partition_json; do local name=$(echo "$partition_json" | jq -r '.name') local size=$(echo "$partition_json" | jq -r '.size') local fs_type=$(echo "$partition_json" | jq -r '.filesystem_type // "raw"') # Convert size based on filesystem type local converted_size="" case "$fs_type" in "ubi"|"jffs2"|"squashfs") converted_size=$(convert_partition_size "$size" "$fs_type") ;; "ext4") local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') case "$unit" in "MB"|"M") converted_size="${numeric%.*}" ;; "KB"|"K") converted_size=$((${numeric%.*} / 1024)) ;; "GB"|"G") converted_size=$((${numeric%.*} * 1024)) ;; *) converted_size="$numeric" ;; esac ;; "raw"|*) local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') case "$unit" in "MB"|"M") converted_size="${numeric%.*}" ;; "KB"|"K") converted_size=$((${numeric%.*} / 1024)) ;; "GB"|"G") converted_size=$((${numeric%.*} * 1024)) ;; *) converted_size="$numeric" ;; esac ;; esac echo "${name},${fs_type},${converted_size}" | tr -d '\r' >> "$partition_info_file" done < <(jq -c '.partition_table[]' "$CONFIG_FILE") # Add terminator echo "::" >> "$partition_info_file" fi print_info "Generated partition-info.txt successfully with selective filesystem-aware size conversion (permissions preserved)" } # Convert size strings like "18MB", "128KB" to hex values convert_size_to_hex() { local size="$1" # Extract numeric value and unit local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') # Convert to bytes then to hex case "$unit" in "MB"|"M") printf "0x%x" $((${numeric%.*} * 1024 * 1024)) ;; "KB"|"K") printf "0x%x" $((${numeric%.*} * 1024)) ;; "GB"|"G") printf "0x%x" $((${numeric%.*} * 1024 * 1024 * 1024)) ;; *) # If no unit, assume it's already in bytes printf "0x%x" "${numeric%.*}" ;; esac } # Format hex values with size/offset conversion rules format_size_offset() { local hex_value="$1" local is_size="$2" # true for size, false for offset # Convert hex to decimal local decimal_value=$((hex_value)) # Special case for offset=0: return "0" directly if [ "$is_size" = "false" ] && [ "$decimal_value" -eq 0 ]; then echo "0" return fi # Apply conversion rules if [ $((decimal_value % 0x100000)) -eq 0 ]; then # Divisible by 1MB (0x100000) echo "$((decimal_value / 0x100000))m" elif [ $((decimal_value % 0x400)) -eq 0 ]; then # Divisible by 1KB (0x400) echo "$((decimal_value / 0x400))k" else # Keep hex format echo "$hex_value" fi } # Extract UBI partition entries from script.ini extract_ubi_partitions() { local script_file="$1" if [ ! -f "$script_file" ]; then return 1 fi # Find lines containing .ubi files and extract partition names and sizes grep -E '\.(ubi|ubi\.lzma)' "$script_file" | while read -r line; do # Extract the .ubi filename pattern: name_pagesize_blocksize_size.ubi local ubi_file=$(echo "$line" | grep -oE '[a-zA-Z0-9_-]+_[0-9]+[KM]B_[0-9]+[KM]B_[0-9]+[MG]B\.(ubi|ubi\.lzma)') if [ -n "$ubi_file" ]; then # Extract partition name (everything before first underscore after removing .ubi) local partition_name=$(echo "$ubi_file" | sed 's/_[0-9].*\.ubi.*$//' | sed 's/\.ubi.*$//') echo "$partition_name:$ubi_file" fi done } # Extract mtdparts string from script.ini extract_mtdparts_string() { local script_file="$1" if [ ! -f "$script_file" ]; then return 1 fi # Find mtdparts string enclosed in single quotes local mtdparts_line=$(grep -E "mtdparts=" "$script_file" | head -1) if [ -n "$mtdparts_line" ]; then # Extract device name and partition info local device_name=$(echo "$mtdparts_line" | sed -n "s/.*mtdparts=\([^:]*\):.*/\1/p") echo "$device_name" fi } # Generate mtdparts string from partition_table generate_mtdparts_string() { local device_name="$1" if [ -z "$device_name" ]; then device_name="spi_nfc" # Default device name fi # Read partition_table from CONFIG_FILE using jq local partition_count=$(jq '.partition_table | length' "$CONFIG_FILE") local mtdparts_entries="" for ((i=0; i> "$temp_file" fi fi print_info "Added new CONFIG_MTDPARTS_DEFAULT" fi } # Validate the updated defconfig contains proper mtdparts validate_defconfig_update() { local defconfig_file="$1" local expected_mtdparts="$2" # Check if CONFIG_BOOTARGS contains the mtdparts local bootargs_mtdparts=$(grep "^CONFIG_BOOTARGS=" "$defconfig_file" | grep -o 'mtdparts=[^"]*') # Check if CONFIG_MTDPARTS_DEFAULT exists local mtdparts_default=$(grep "^CONFIG_MTDPARTS_DEFAULT=" "$defconfig_file") if [ -n "$bootargs_mtdparts" ] || [ -n "$mtdparts_default" ]; then print_info "Validation successful: mtdparts configuration found in defconfig" return 0 else print_error "Validation failed: mtdparts configuration not properly updated" return 1 fi } # Update mtdparts portion within CONFIG_BOOTARGS in header file update_header_bootargs() { local header_file="$1" local new_mtdparts_str="$2" local temp_file="$3" local device_name="$4" # Check if CONFIG_BOOTARGS exists if grep -q "^#define CONFIG_BOOTARGS" "$header_file"; then # Check if mtdparts exists in CONFIG_BOOTARGS if grep "^#define CONFIG_BOOTARGS" "$header_file" | grep -q "mtdparts="; then # Delete content after mtdparts=device_name: until the closing quote, then add new content # This handles both single-line and multi-line definitions sed -i '/^#define CONFIG_BOOTARGS/,/[^\\]$/{ s|mtdparts=[^:]*:[^"]*"|mtdparts='"${device_name}"':'"${new_mtdparts_str}"'"|g }' "$temp_file" print_info "Updated mtdparts in CONFIG_BOOTARGS" else # No mtdparts found, add it before the closing quote # Remove trailing quote and backslashes, add mtdparts, then add quote back sed -i '/^#define CONFIG_BOOTARGS/,/[^\\]$/{ s|"[[:space:]]*$| mtdparts='"${device_name}"':'"${new_mtdparts_str}"'"| }' "$temp_file" print_info "Added mtdparts to CONFIG_BOOTARGS" fi else print_warning "No CONFIG_BOOTARGS found in header file" fi } # Update MTDPARTS_DEFAULT in header file update_header_mtdparts_default() { local header_file="$1" local new_mtdparts_str="$2" local temp_file="$3" local device_name="$4" # Check if MTDPARTS_DEFAULT exists if grep -q "^#define MTDPARTS_DEFAULT" "$header_file"; then # Check if mtdparts exists in MTDPARTS_DEFAULT if grep "^#define MTDPARTS_DEFAULT" "$header_file" | grep -q "mtdparts="; then # Delete content after mtdparts=device_name: until the closing quote, then add new content # This handles both single-line and multi-line definitions sed -i '/^#define MTDPARTS_DEFAULT/,/[^\\]$/{ s|mtdparts=[^:]*:[^"]*"|mtdparts='"${device_name}"':'"${new_mtdparts_str}"'"|g }' "$temp_file" print_info "Updated mtdparts in MTDPARTS_DEFAULT" else # No mtdparts found, add it before the closing quote sed -i '/^#define MTDPARTS_DEFAULT/,/[^\\]$/{ s|"[[:space:]]*$|mtdparts='"${device_name}"':'"${new_mtdparts_str}"'"| }' "$temp_file" print_info "Added mtdparts to MTDPARTS_DEFAULT" fi else # Add new MTDPARTS_DEFAULT line if it doesn't exist echo "#define MTDPARTS_DEFAULT \"mtdparts=${device_name}:${new_mtdparts_str}\"" >> "$temp_file" print_info "Added new MTDPARTS_DEFAULT" fi } # Update defconfig based on platform update_defconfig() { # Skip defconfig processing for los_emmc medium type if [ "$MEDIUM_TYPE" = "los_emmc" ]; then print_info "Skipping defconfig update for los_emmc medium type" return 0 fi # Skip defconfig processing for non-qm10xd platforms if [ "$PLATFORM" = "qm10xv" ]; then print_info "Skipping defconfig update for platform: $PLATFORM (only qm10xv qm10xh supported)" return 0 fi local defconfig_file="" case "$PLATFORM" in "qm10xd") defconfig_file="$ROOT_DIR/base/soc/qm10xd/$OS_TYPE/bsp/uboot/uboot/configs/quaming_qm10xd_${CONFIG_NAME}_defconfig" ;; "qm10xv") defconfig_file="$ROOT_DIR/base/soc/qm10xv/$OS_TYPE/board_support/uboot/uboot/configs/qua_10xv_${CONFIG_NAME}_defconfig" ;; "qm10xh") defconfig_file="$ROOT_DIR/base/soc/qm10xh/$OS_TYPE/bsp/uboot/uboot/configs/quaming_qm108h_${CONFIG_NAME}_defconfig" ;; *) print_error "Unsupported platform: $PLATFORM" exit 1 ;; esac print_info "Updating defconfig: $defconfig_file" # Validate defconfig file exists if [ ! -f "$defconfig_file" ]; then print_error "Defconfig file not found: $defconfig_file" print_info "Cannot update non-existent defconfig file" return 1 fi # Validate JSON configuration exists and contains partition_table if [ ! -f "$CONFIG_FILE" ]; then print_error "Configuration file not found: $CONFIG_FILE" return 1 fi local partition_count=$(jq '.partition_table | length' "$CONFIG_FILE" 2>/dev/null) if [ -z "$partition_count" ] || [ "$partition_count" -eq 0 ]; then print_error "No valid partition_table found in configuration file" return 1 fi # Create backup local backup_file="${defconfig_file}.backup" cp "$defconfig_file" "$backup_file" print_info "Created backup: $backup_file" # Extract device name from existing defconfig local device_name=$(extract_device_name_from_defconfig "$defconfig_file") print_info "Detected MTD device name: $device_name" # Generate new mtdparts string using existing function (for CONFIG_BOOTARGS) local new_mtdparts=$(generate_mtdparts_string "$device_name") if [ -z "$new_mtdparts" ]; then print_error "Failed to generate mtdparts string" return 1 fi print_info "Generated mtdparts for CONFIG_BOOTARGS: $new_mtdparts" # Generate mtdparts string for CONFIG_MTDPARTS_DEFAULT (without offsets) local new_mtdparts_default=$(generate_mtdparts_default_string "$device_name") if [ -z "$new_mtdparts_default" ]; then print_error "Failed to generate mtdparts default string" return 1 fi print_info "Generated mtdparts for CONFIG_MTDPARTS_DEFAULT: $new_mtdparts_default" # Create temporary file for processing local temp_file=$(mktemp) cp "$defconfig_file" "$temp_file" # Update CONFIG_BOOTARGS mtdparts portion update_bootargs_mtdparts "$defconfig_file" "$new_mtdparts" "$temp_file" # Update CONFIG_MTDPARTS_DEFAULT update_mtdparts_default "$defconfig_file" "$new_mtdparts_default" "$temp_file" # Replace original file and preserve permissions chmod --reference="$defconfig_file" "$temp_file" 2>/dev/null || true mv "$temp_file" "$defconfig_file" # Validate the update validate_defconfig_update "$defconfig_file" "$new_mtdparts" local validation_result=$? if [ $validation_result -eq 0 ]; then print_info "Updated defconfig successfully with mtdparts configuration" else print_warning "Defconfig updated but validation failed - please verify manually" fi return $validation_result } # Convert size strings like "18MB", "128KB" to bytes for DTS format convert_size_to_bytes() { local size="$1" # Extract numeric value and unit local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') # Convert to bytes case "$unit" in "MB"|"M") printf "0x%x" $((${numeric%.*} * 1024 * 1024)) ;; "KB"|"K") printf "0x%x" $((${numeric%.*} * 1024)) ;; "GB"|"G") printf "0x%x" $((${numeric%.*} * 1024 * 1024 * 1024)) ;; *) # If no unit, assume it's already in hex format or bytes if [[ "$size" =~ ^0x[0-9a-fA-F]+$ ]]; then echo "$size" else printf "0x%x" "${numeric%.*}" fi ;; esac } # Update header files based on partition configuration update_header_files() { # Only process qm10xv platform if [ "$PLATFORM" != "qm10xv" ]; then print_info "Skipping header file update for platform: $PLATFORM (only qm10xv supported)" return 0 fi # Skip if medium type is los_emmc if [ "$MEDIUM_TYPE" = "los_emmc" ]; then print_info "Skipping header file update for medium type: $MEDIUM_TYPE" return 0 fi # Only process los_spi-nand and los_spi-nor medium types if [ "$MEDIUM_TYPE" != "los_spi-nand" ] && [ "$MEDIUM_TYPE" != "los_spi-nor" ]; then print_info "Skipping header file update for medium type: $MEDIUM_TYPE (only los_spi-nand and los_spi-nor supported)" return 0 fi print_info "Updating qm102v.h for platform: $PLATFORM, medium type: $MEDIUM_TYPE" # Determine qm102v.h file path local header_file="$ROOT_DIR/base/soc/$PLATFORM/linux/board_support/uboot/uboot/board/fullhan/qm102v/$CONFIG_NAME/qm102v.h" print_info "Target header file: $header_file" # Check if header file exists if [ ! -f "$header_file" ]; then print_warning "Header file not found: $header_file" return 0 fi # Validate JSON configuration if ! jq -e '.partition_table' "$CONFIG_FILE" >/dev/null 2>&1; then print_error "Invalid JSON configuration: missing partition_table" return 1 fi # Create backup local backup_file="${header_file}.backup" cp "$header_file" "$backup_file" print_info "Created backup: $backup_file" # Generate mtdparts string from JSON configuration local mtdparts_str="" local first_partition=true # Get total partition count to identify the last partition local total_partitions=$(jq '.partition_table | length' "$CONFIG_FILE") local current_partition=0 while IFS= read -r partition_json; do current_partition=$((current_partition + 1)) local name=$(echo "$partition_json" | jq -r '.name') local size=$(echo "$partition_json" | jq -r '.size') local offset=$(echo "$partition_json" | jq -r '.offset') # Convert size for mtdparts format local mtd_size="" if [[ "$size" =~ ([0-9]+)KB ]]; then local kb_val="${BASH_REMATCH[1]}" mtd_size="${kb_val}k" elif [[ "$size" =~ ([0-9]+)MB ]]; then local mb_val="${BASH_REMATCH[1]}" mtd_size="${mb_val}m" elif [[ "$size" =~ ([0-9]+\.?[0-9]*)MB ]]; then local mb_val="${BASH_REMATCH[1]}" # Convert decimal MB to KB local kb_val=$(echo "$mb_val * 1024" | bc) mtd_size="${kb_val%.*}k" else # Use size as-is for special cases mtd_size="$size" fi # Convert offset for mtdparts format (for CONFIG_BOOTARGS) local mtd_offset="" if [[ "$offset" =~ 0x([0-9a-fA-F]+) ]]; then # Use format_size_offset function to convert offset properly mtd_offset=$(format_size_offset "$offset" "false") else mtd_offset="$offset" fi # Handle last partition (use '-' for size to indicate remaining space) if [ $current_partition -eq $total_partitions ]; then mtd_size="-" fi # Build mtdparts string for CONFIG_BOOTARGS (with size@offset format) if [ "$first_partition" = true ]; then mtdparts_str="${mtd_size}@${mtd_offset}(${name})" first_partition=false else mtdparts_str="${mtdparts_str},${mtd_size}@${mtd_offset}(${name})" fi done < <(jq -r '.partition_table[] | @json' "$CONFIG_FILE") # Generate simple mtdparts string for MTDPARTS_DEFAULT (only size and name) local mtdparts_default_str=$mtdparts_str print_info "Generated mtdparts for CONFIG_BOOTARGS: $mtdparts_str" print_info "Generated mtdparts for MTDPARTS_DEFAULT: $mtdparts_default_str" # Determine device name based on medium type local device_name="spi0.0" # Preserve file permissions local file_perms=$(stat -c "%a" "$header_file") local temp_file="${header_file}.tmp" # Create temp file for processing cp "$header_file" "$temp_file" # Update MTDPARTS_DEFAULT using helper function update_header_mtdparts_default "$header_file" "$mtdparts_default_str" "$temp_file" "$device_name" # Update CONFIG_BOOTARGS using helper function update_header_bootargs "$header_file" "$mtdparts_str" "$temp_file" "$device_name" # Replace original file with modified version mv "$temp_file" "$header_file" # Restore file permissions chmod "$file_perms" "$header_file" print_info "Header file updated: $(basename "$header_file")" print_info "Header file update completed successfully" return 0 } # Generate a random GUID in standard format generate_guid() { local hex_chars="0123456789ABCDEF" local guid="" # Generate 32 hex characters for i in {1..32}; do guid="${guid}${hex_chars:$((RANDOM % 16)):1}" done # Format as GUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx echo "${guid:0:8}-${guid:8:4}-${guid:12:4}-${guid:16:4}-${guid:20:12}" } # Convert size to KB for GPT XML convert_size_to_kb() { local size="$1" # Extract numeric value and unit local numeric=$(echo "$size" | sed 's/[^0-9.]//g') local unit=$(echo "$size" | sed 's/[0-9.]//g' | tr '[:lower:]' '[:upper:]') # Convert to KB case "$unit" in "MB"|"M") echo $((${numeric%.*} * 1024)) ;; "KB"|"K") echo "${numeric%.*}" ;; "GB"|"G") echo $((${numeric%.*} * 1024 * 1024)) ;; *) # If no unit, assume it's already in KB echo "${numeric%.*}" ;; esac } # Update programmer.ini file based on partition-config.json update_programmer_ini() { print_info "Checking programmer.ini update requirements..." # Rule A: Skip if medium type is los_emmc if [ "$MEDIUM_TYPE" = "los_emmc" ]; then print_info "Skipping programmer.ini update: medium type is los_emmc" return 0 fi # Construct programmer.ini path based on platform, medium_type, and project_name local programmer_ini_path="$ROOT_DIR/tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" print_info "Checking for programmer.ini at: $programmer_ini_path" # Rule B: Skip if programmer.ini doesn't exist in project directory if [ ! -f "$programmer_ini_path" ]; then print_info "Skipping programmer.ini update: file not found in project directory" return 0 fi print_info "Found programmer.ini file, proceeding with update..." # Validate JSON configuration exists if [ ! -f "$CONFIG_FILE" ]; then print_error "Configuration file not found: $CONFIG_FILE" return 1 fi # Create backup local backup_file="${programmer_ini_path}.backup" cp "$programmer_ini_path" "$backup_file" print_info "Created backup: $backup_file" # Determine offset type based on medium type local offset_field="" local offset_unit="" if [ "$MEDIUM_TYPE" = "los_spi-nand" ]; then # For spi-nand: use BlockOffset (offset / 128KB) offset_field="BlockOffset" offset_unit="blocks" local block_size_128kb=$((128 * 1024)) print_info "Using fixed block size: 128KB ($block_size_128kb bytes) for BlockOffset calculation" elif [ "$MEDIUM_TYPE" = "los_spi-nor" ]; then # For spi-nor: use BufferOffset (direct address) offset_field="BufferOffset" offset_unit="bytes" print_info "Using BufferOffset for direct address update (no block conversion)" else print_warning "Unknown medium type: $MEDIUM_TYPE, defaulting to BlockOffset" offset_field="BlockOffset" offset_unit="blocks" local block_size_128kb=$((128 * 1024)) fi # Create temporary file for processing local temp_file="${programmer_ini_path}.tmp" cp "$programmer_ini_path" "$temp_file" # Parse partition_table from partition-config.json and create a mapping declare -A partition_offsets while IFS= read -r partition_json; do local name=$(echo "$partition_json" | jq -r '.name') local offset=$(echo "$partition_json" | jq -r '.offset') # Convert offset to decimal local offset_decimal=$((offset)) # Calculate offset value based on medium type local offset_value if [ "$MEDIUM_TYPE" = "los_spi-nand" ]; then # Calculate BlockOffset: offset / 128KB offset_value=$((offset_decimal / block_size_128kb)) print_info "Partition '$name': offset=$offset, BlockOffset=$offset_value" elif [ "$MEDIUM_TYPE" = "los_spi-nor" ]; then # Convert offset to hex format with 'h' suffix (e.g., 0x100000 -> 100000h) # Remove 0x prefix and add h suffix offset_value=$(printf "%x" $offset_decimal) offset_value="${offset_value}h" print_info "Partition '$name': offset=$offset, BufferOffset=$offset_value" fi # Store in associative array partition_offsets["$name"]=$offset_value done < <(jq -c '.partition_table[]' "$CONFIG_FILE") # Rule C: Update offset field in programmer.ini by matching PartitionName local section="" local partition_name="" local updates_made=0 local lines_processed=0 while IFS= read -r line; do lines_processed=$((lines_processed + 1)) # Detect section headers like [FILE0], [FILE1], etc. if [[ "$line" =~ ^\[FILE[0-9]+\]$ ]]; then section="$line" partition_name="" echo "$line" >> "$temp_file.new" continue fi # Extract PartitionName if [[ "$line" =~ ^PartitionName[[:space:]]*=[[:space:]]*(.+)$ ]]; then partition_name=$(echo "${BASH_REMATCH[1]}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') echo "$line" >> "$temp_file.new" continue fi # Update BlockOffset if we have a matching partition name and medium type is los_spi-nand if [ "$MEDIUM_TYPE" = "los_spi-nand" ] && [[ "$line" =~ ^BlockOffset[[:space:]]*=[[:space:]]*(.+)$ ]] && [ -n "$partition_name" ]; then local old_offset=$(echo "${BASH_REMATCH[1]}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') # Check if this partition exists in our mapping if [ -n "${partition_offsets[$partition_name]}" ]; then local new_offset="${partition_offsets[$partition_name]}" if [ "$old_offset" != "$new_offset" ]; then echo "BlockOffset =$new_offset" >> "$temp_file.new" print_info "Updated partition '$partition_name': BlockOffset $old_offset -> $new_offset" updates_made=$((updates_made + 1)) else echo "$line" >> "$temp_file.new" print_info "Partition '$partition_name': BlockOffset unchanged ($new_offset)" fi else # Partition not found in JSON, keep original line echo "$line" >> "$temp_file.new" print_warning "Partition '$partition_name' not found in partition-config.json, keeping original BlockOffset" fi # Reset partition_name after processing BlockOffset partition_name="" continue fi # Update BufferOffset if we have a matching partition name and medium type is los_spi-nor if [ "$MEDIUM_TYPE" = "los_spi-nor" ] && [[ "$line" =~ ^BufferOffset[[:space:]]*=[[:space:]]*(.+)$ ]] && [ -n "$partition_name" ]; then local old_offset=$(echo "${BASH_REMATCH[1]}" | sed 's/^[[:space:]]*//;s/[[:space:]]*$//') # Check if this partition exists in our mapping if [ -n "${partition_offsets[$partition_name]}" ]; then local new_offset="${partition_offsets[$partition_name]}" if [ "$old_offset" != "$new_offset" ]; then echo "BufferOffset =$new_offset" >> "$temp_file.new" print_info "Updated partition '$partition_name': BufferOffset $old_offset -> $new_offset" updates_made=$((updates_made + 1)) else echo "$line" >> "$temp_file.new" print_info "Partition '$partition_name': BufferOffset unchanged ($new_offset)" fi else # Partition not found in JSON, keep original line echo "$line" >> "$temp_file.new" print_warning "Partition '$partition_name' not found in partition-config.json, keeping original BufferOffset" fi # Reset partition_name after processing BufferOffset partition_name="" continue fi # Copy all other lines as-is echo "$line" >> "$temp_file.new" done < "$temp_file" # Replace original file with updated version if [ -f "$temp_file.new" ]; then # Preserve file permissions chmod --reference="$programmer_ini_path" "$temp_file.new" 2>/dev/null || true mv "$temp_file.new" "$programmer_ini_path" rm -f "$temp_file" if [ $updates_made -gt 0 ]; then print_info "Successfully updated programmer.ini: $updates_made $offset_field values changed" else print_info "programmer.ini checked: all $offset_field values are up to date" fi else print_error "Failed to create updated programmer.ini file" rm -f "$temp_file" return 1 fi print_info "programmer.ini update completed" return 0 } # Update GPT partition table for los_emmc medium update_gpt() { # Check if medium type is los_emmc if [ "$MEDIUM_TYPE" != "los_emmc" ]; then print_info "Skipping GPT update: medium type is not los_emmc" return 0 fi # Check if platform is qm10xh if [ "$PLATFORM" != "qm10xh" ]; then print_info "Skipping GPT update: platform is not qm10xh" return 0 fi print_info "Updating GPT partition table for los_emmc on qm10xh" # Construct XML file path based on medium size (keep uppercase) local xml_dir="$ROOT_DIR/tools/partition/qm10xh/los_emmc/$PROJECT_NAME" local xml_file="$xml_dir/qm108h_los_emmc_partition.xml" print_info "Target XML file: $xml_file" # Check if XML file exists if [ ! -f "$xml_file" ]; then print_error "GPT XML file not found: $xml_file" return 1 fi # Create backup local backup_file="${xml_file}.backup" cp "$xml_file" "$backup_file" print_info "Created backup: $backup_file" # Read existing partitions from XML to preserve GUIDs declare -A existing_partitions declare -A existing_guids declare -A existing_sizes # Parse XML to get existing partition labels, GUIDs, and sizes while IFS= read -r line; do if [[ "$line" =~ \ ]]; then xml_header="${xml_header}${line} " in_header=false continue fi if [[ "$line" =~ \ ]]; then in_footer=true xml_footer="${xml_footer}${line} " continue fi if [ "$in_header" = true ]; then xml_header="${xml_header}${line} " elif [ "$in_footer" = true ]; then xml_footer="${xml_footer}${line} " fi done < "$xml_file" # Build new partition entries based on partition-config.json order local new_partition_entries="" local partitions_updated=0 local partitions_matched=0 local partitions_added=0 while IFS= read -r partition_json; do local name=$(echo "$partition_json" | jq -r '.name') local size=$(echo "$partition_json" | jq -r '.size') local filename=$(echo "$partition_json" | jq -r '.filename // empty') local fs_type=$(echo "$partition_json" | jq -r '.filesystem_type // "raw"') local sparse_attr=$(echo "$partition_json" | jq -r '.sparse // false') # Convert size to KB local size_kb=$(convert_size_to_kb "$size") # Determine sparse attribute based on filesystem type local sparse_value="false" if [ "$fs_type" != "raw" ] || [ "$sparse_attr" = "true" ]; then sparse_value="true" fi # Determine filename if [ -z "$filename" ]; then if [ "$fs_type" = "raw" ] || [ "$name" = "reserve" ] || [ "$name" = "padding" ]; then filename="" else filename="${name}.img" fi fi # Check if partition exists in original XML local guid="" local status="" if [ -n "${existing_partitions[$name]}" ]; then # Partition exists, reuse GUID guid="${existing_guids[$name]}" if [ "${existing_sizes[$name]}" != "$size_kb" ]; then # Size changed status="updated" print_info "Partition '$name' size changed: ${existing_sizes[$name]}KB -> ${size_kb}KB" partitions_updated=$((partitions_updated + 1)) else # Size matches status="matched" print_info "Partition '$name' size matches: ${size_kb}KB (no change)" partitions_matched=$((partitions_matched + 1)) fi else # New partition, generate new GUID guid=$(generate_guid) status="added" print_info "New partition '$name' added: ${size_kb}KB (sparse=$sparse_value)" partitions_added=$((partitions_added + 1)) fi # Build partition line local partition_line=" " if [ -z "$new_partition_entries" ]; then new_partition_entries="$partition_line" else new_partition_entries="${new_partition_entries} ${partition_line}" fi done <<< "$(jq -c '.partition_table[]' "$CONFIG_FILE")" # Create new XML file local temp_file="${xml_file}.tmp" # Write XML header printf "%s" "$xml_header" > "$temp_file" # Write all partition entries printf "%s\n" "$new_partition_entries" >> "$temp_file" # Write XML footer printf "%s" "$xml_footer" >> "$temp_file" # Replace original file with rebuilt version mv "$temp_file" "$xml_file" print_info "GPT XML file rebuilt successfully" print_info "Summary: $partitions_matched matched, $partitions_updated updated, $partitions_added added" # Generate gpt.img using ptool.py print_info "Generating gpt.img using ptool.py..." local ptool_script="$ROOT_DIR/tools/partition/emmc_partition_tool/ptool.py" if [ ! -f "$ptool_script" ]; then print_error "ptool.py not found: $ptool_script" return 1 fi # Run ptool.py with Python 2 local ptool_cmd="python2 $ptool_script -x $xml_file -t $xml_dir/" print_info "Running: $ptool_cmd" if $ptool_cmd; then print_info "GPT image generated successfully" else print_error "Failed to generate GPT image" return 1 fi # Rename and cleanup generated files print_info "Renaming generated files..." # Rename gpt_main0.bin to gpt_qm108h_los_emmc.bin if [ -f "${xml_dir}/gpt_main0.bin" ]; then mv "${xml_dir}/gpt_main0.bin" "${xml_dir}/gpt_qm108h_los_emmc.bin" print_info "Renamed: gpt_main0.bin -> gpt_qm108h_los_emmc.bin" else print_warning "gpt_main0.bin not found, skipping rename" fi # Rename rawprogram0.xml to qm108h_los_emmc_partition_o.xml if [ -f "${xml_dir}/rawprogram0.xml" ]; then mv "${xml_dir}/rawprogram0.xml" "${xml_dir}/qm108h_los_emmc_partition_o.xml" print_info "Renamed: rawprogram0.xml -> qm108h_los_emmc_partition_o.xml" else print_warning "rawprogram0.xml not found, skipping rename" fi # Change permissions for all files generated by ptool.py (set to 644) print_info "Setting file permissions for generated files..." # Find all files in xml_dir (excluding backup files) for file in "$xml_dir"/*; do if [ -f "$file" ]; then local filename=$(basename "$file") # Skip backup files if [[ ! "$filename" =~ \.backup$ ]]; then chmod 775 "$file" 2>/dev/null || true fi fi done print_info "File permissions set to 775 for all generated files" print_info "GPT update completed successfully" return 0 } # Main execution main() { print_info "=== XOS Partition Configuration Update ===" # Check dependencies check_dependencies # Validate configuration validate_config # Display configuration summary print_info "Configuration Summary:" print_info " Project: $PROJECT_NAME_CONFIG" print_info " Config: $CONFIG_NAME" print_info " Platform: $PLATFORM" print_info " Medium: $MEDIUM_TYPE ($MEDIUM_SIZE)" print_info " OS: $OS_TYPE" # Generate files generate_partition_info update_defconfig update_header_files update_programmer_ini update_gpt print_info "=== Partition configuration update completed successfully ===" print_info "Modified files:" print_info " - product/$PROJECT_NAME/partition-info.txt" case "$PLATFORM" in "qm10xd") print_info " - base/soc/qm10xd/$OS_TYPE/bsp/uboot/uboot/configs/quaming_qm10xd_${CONFIG_NAME}_defconfig" if [ "$MEDIUM_TYPE" != "los_emmc" ] && [ -f "$ROOT_DIR/tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" ]; then print_info " - tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" fi ;; "qm10xv") print_info " - base/soc/qm10xv/$OS_TYPE/board_support/uboot/uboot/board/fullhan/qm102v/$CONFIG_NAME/qm102v.h" if [ "$MEDIUM_TYPE" != "los_emmc" ] && [ -f "$ROOT_DIR/tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" ]; then print_info " - tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" fi ;; "qm10xh") if [ "$MEDIUM_TYPE" = "los_emmc" ]; then print_info " - tools/partition/qm10xh/los_emmc/$PROJECT_NAME/qm108h_los_emmc_partition.xml" print_info " - tools/partition/qm10xh/los_emmc/$PROJECT_NAME/gpt_qm108h_los_emmc.bin" print_info " - tools/partition/qm10xh/los_emmc/$PROJECT_NAME/qm108h_los_emmc_partition_o.xml" else print_info " - base/soc/qm10xh/$OS_TYPE/bsp/uboot/uboot/configs/quaming_qm108h_${CONFIG_NAME}_defconfig" if [ -f "$ROOT_DIR/tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" ]; then print_info " - tools/partition/$PLATFORM/$MEDIUM_TYPE/$PROJECT_NAME_CONFIG/programmer.ini" fi fi ;; esac print_info "" print_info "Next steps:" print_info " 1. Review the generated files" print_info " 2. Run your build command" print_info " 3. Test the new partition configuration" } # Execute main function main "$@"