On 03.11.25 14:12, Philippe Mathieu-Daudé wrote:
> Hi Jan,
>
> On 17/10/25 14:03, Jan Kiszka wrote:
>> From: Jan Kiszka <[email protected]>
>>
>> As an eMMC block device image may consist of more than just the user
>> data partition, provide a helper script that can compose the image from
>> boot partitions, an RPMB partition and the user data image. The script
>> also does the required size validation and/or rounding.
>>
>> Signed-off-by: Jan Kiszka <[email protected]>
>> ---
>> scripts/mkemmc.sh | 218 ++++++++++++++++++++++++++++++++++++++++++++++
>> 1 file changed, 218 insertions(+)
>> create mode 100755 scripts/mkemmc.sh
>>
>> diff --git a/scripts/mkemmc.sh b/scripts/mkemmc.sh
>> new file mode 100755
>> index 0000000000..1a2b7a6193
>> --- /dev/null
>> +++ b/scripts/mkemmc.sh
>> @@ -0,0 +1,218 @@
>> +#!/bin/sh -e
>> +# SPDX-License-Identifier: GPL-2.0-only
>> +#
>> +# Create eMMC block device image from boot, RPMB and user data images
>> +#
>> +# Copyright (c) Siemens, 2025
>> +#
>> +# Authors:
>> +# Jan Kiszka <[email protected]>
>> +#
>> +# This work is licensed under the terms of the GNU GPL version 2.
>> +# See the COPYING file in the top-level directory.
>> +#
>> +
>> +usage() {
>> + echo "$0 [OPTIONS] USER_IMG[:SIZE] OUTPUT_IMG"
>> + echo ""
>> + echo "SIZE must be a power of 2 up to 2G and multiples of 512
>> byte from there on."
>> + echo "If no SIZE is specified, the size of USER_ING will be used
>> (rounded up)."
>> + echo ""
>> + echo "Supported options:"
>> + echo " -b BOOT1_IMG[:SIZE] Add boot partitions. SIZE must be
>> multiples of 128K. If"
>> + echo " no SIZE is specified, the size of
>> BOOT1_IMG will be"
>> + echo " used (rounded up). BOOT1_IMG will
>> be stored in boot"
>> + echo " partition 1, and a boot partition
>> 2 of the same size"
>> + echo " will be created as empty (all
>> zeros) unless -B is"
>> + echo " specified as well."
>> + echo " -B BOOT2_IMG Fill boot partition 2 with
>> BOOT2_IMG. Must be combined"
>> + echo " with -b which is also defining
>> the partition size."
>> + echo " -r RPMB_IMG[:SIZE] Add RPMB partition. SIZE must be
>> multiples of 128K. If"
>> + echo " no SIZE is specified, the size of
>> RPMB_IMG will be"
>> + echo " used (rounded up)."
>> + echo " -h, --help This help"
>> + echo ""
>> + echo "All SIZE parameters support the units K, M, G. If SIZE is
>> smaller than the"
>> + echo "associated image, it will be truncated in the output image."
>> + exit "$1"
>> +}
>> +
>> +process_size() {
>> + name=$1
>> + image_file=$2
>> + alignment=$3
>> + image_arg=$4
>> + if [ "${image_arg#*:}" = "$image_arg" ]; then
>> + if ! size=$(stat -L -c %s "$image_file" 2>/dev/null); then
>> + echo "Missing $name image '$image_file'." >&2
>> + exit 1
>> + fi
>> + if [ "$alignment" = 128 ]; then
>> + size=$(( (size + 128 * 1024 - 1) & ~(128 * 1024 - 1) ))
>> + elif [ $size -gt $((2 * 1024 * 1024 * 1024)) ]; then
>> + size=$(( (size + 511) & ~511 ))
>> + elif [ $(( size & (size - 1) )) -gt 0 ]; then
>> + n=0
>> + while [ "$size" -gt 0 ]; do
>> + size=$((size >> 1))
>> + n=$((n + 1))
>> + done
>> + size=$((1 << n))
>> + fi
>> + else
>> + value="${image_arg#*:}"
>> + if [ "${value%K}" != "$value" ]; then
>> + size=${value%K}
>> + multiplier=1024
>> + elif [ "${value%M}" != "$value" ]; then
>> + size=${value%M}
>> + multiplier=$((1024 * 1024))
>> + elif [ "${value%G}" != "$value" ]; then
>> + size=${value%G}
>> + multiplier=$((1024 * 1024 * 1024))
>> + else
>> + size=$value
>> + multiplier=1
>> + fi
>> + if [ "$size" -eq "$size" ] 2>/dev/null; then
>
> I don't get this check, should one be "$value"?
>
Likely deserves a comment, I had to refresh my own memory as well:
This checks if $size is a valid integer value. If we just run the
multiplication below, we won't be able to react properly.
>> + size=$((size * multiplier))
>> + else
>> + echo "Invalid value '$value' specified for $image_file
>> image size." >&2
>> + exit 1
>> + fi
>> + if [ "$alignment" = 128 ]; then
>> + if [ $(( size & (128 * 1024 - 1) )) -ne 0 ]; then
>> + echo "The $name image size must be multiples of
>> 128K." >&2
>> + exit 1
>> + fi
>> + elif [ $size -gt $((2 * 1024 * 1024 * 1024)) ]; then
>> + if [ $(( size & 511)) -ne 0 ]; then
>> + echo "The $name image size must be multiples of 512
>> (if >2G)." >&2
>> + exit 1
>> + fi
>> + elif [ $(( size & (size - 1) )) -gt 0 ]; then
>> + echo "The $name image size must be power of 2 (up to
>> 2G)." >&2
>> + exit 1
>> + fi
>> + fi
>> + echo $size
>> +}
>> +
>> +check_truncation() {
>> + image_file=$1
>> + output_size=$2
>> + if [ "$image_file" = "/dev/zero" ]; then
>> + return
>> + fi
>> + if ! actual_size=$(stat -L -c %s "$image_file" 2>/dev/null); then
>> + echo "Missing image '$image_file'." >&2
>> + exit 1
>> + fi
>> + if [ "$actual_size" -gt "$output_size" ]; then
>> + echo "Warning: image '$image_file' will be truncated on output."
>> + fi
>> +}
>> +
>> +userimg=
>> +outimg=
>> +bootimg1=
>> +bootimg2=/dev/zero
>> +bootsz=0
>> +rpmbimg=
>> +rpmbsz=0
>> +
>> +while [ $# -gt 0 ]; do
>> + case "$1" in
>> + -b)
>> + shift
>> + [ $# -ge 1 ] || usage 1
>> + bootimg1=${1%%:*}
>> + bootsz=$(process_size boot "$bootimg1" 128 "$1")
>> + shift
>> + ;;
>> + -B)
>> + shift
>> + [ $# -ge 1 ] || usage 1
>> + bootimg2=$1
>> + shift
>> + ;;
>> + -r)
>> + shift
>> + [ $# -ge 1 ] || usage 1
>> + rpmbimg=${1%%:*}
>> + rpmbsz=$(process_size RPMB "$rpmbimg" 128 "$1")
>> + shift
>> + ;;
>> + -h|--help)
>> + usage 0
>> + ;;
>> + *)
>> + if [ -z "$userimg" ]; then
>> + userimg=${1%%:*}
>> + usersz=$(process_size user "$userimg" U "$1")
>> + elif [ -z "$outimg" ]; then
>> + outimg=$1
>> + else
>> + usage 1
>> + fi
>> + shift
>> + ;;
>> + esac
>> +done
>> +
>> +[ -n "$outimg" ] || usage 1
>> +
>> +if [ "$bootsz" -gt $((32640 * 1024)) ]; then
>
> Running on macOS:
>
> scripts/mkemmc.sh: line 165: [: : integer expression expected
> scripts/mkemmc.sh: line 169: [: : integer expression expected
> scripts/mkemmc.sh: line 179: [: : integer expression expected
> scripts/mkemmc.sh: line 191: [: : integer expression expected
> scripts/mkemmc.sh: line 200: [: : integer expression expected
> scripts/mkemmc.sh: line 202: [: : integer expression expected
> scripts/mkemmc.sh: line 204: [: : integer expression expected
>
> $ sh --version
> GNU bash, version 3.2.57(1)-release (arm64-apple-darwin24)
>
> When using dash:
>
> scripts/mkemmc.sh: 165: [: Illegal number:
> scripts/mkemmc.sh: 169: [: Illegal number:
> scripts/mkemmc.sh: 179: [: Illegal number:
> scripts/mkemmc.sh: 191: [: Illegal number:
> scripts/mkemmc.sh: 200: [: Illegal number:
> scripts/mkemmc.sh: 202: [: Illegal number:
> scripts/mkemmc.sh: 204: [: Illegal number:
>
> Should we replace s/[/[[/?
No, that would be invalid outside of bash. There must be a logical error.
How did you invoke the script? What was the value of bootsz then?
>
>> + echo "Boot image size is larger than 32640K." >&2
>> + exit 1
>> +fi
>> +if [ "$rpmbsz" -gt $((16384 * 1024)) ]; then
>> + echo "RPMB image size is larger than 16384K." >&2
>> + exit 1
>> +fi
>> +
>> +echo "Creating eMMC image"
>> +
>> +truncate "$outimg" -s 0
> I'd replace here by:
> truncate -s 0 "$outimg"
>
> to avoid on macOS:
>
> usage: truncate [-c] -s [+|-|%|/]size[K|k|M|m|G|g|T|t] file ...
> truncate [-c] -r rfile file ...
>
Will do.
>> +pos=0
>> +
>> +if [ "$bootsz" -gt 0 ]; then
>> + echo " Boot partition 1 and 2: $((bootsz / 1024))K each"
>> + blocks=$(( bootsz / (128 * 1024) ))
>> + check_truncation "$bootimg1" "$bootsz"
>> + dd if="$bootimg1" of="$outimg" conv=sparse bs=128K count=$blocks \
>> + status=none
>> + check_truncation "$bootimg2" "$bootsz"
>> + dd if="$bootimg2" of="$outimg" conv=sparse bs=128K count=$blocks \
>> + seek=$blocks status=none
>> + pos=$((2 * bootsz))
>> +fi
>> +
>> +if [ "$rpmbsz" -gt 0 ]; then
>> + echo " RPMB partition: $((rpmbsz / 1024))K"
>> + blocks=$(( rpmbsz / (128 * 1024) ))
>> + check_truncation "$rpmbimg" "$rpmbsz"
>> + dd if="$rpmbimg" of="$outimg" conv=sparse bs=128K count=$blocks \
>> + seek=$(( pos / (128 * 1024) )) status=none
>> + pos=$((pos + rpmbsz))
>> +fi
>> +
>> +if [ "$usersz" -lt 1024 ]; then
>> + echo " User data: $usersz bytes"
>> +elif [ "$usersz" -lt $((1024 * 1024)) ]; then
>> + echo " User data: $(( (usersz + 1023) / 1024 ))K
>> ($usersz)"
>> +elif [ "$usersz" -lt $((1024 * 1024 * 1024)) ]; then
>> + echo " User data: $(( (usersz + 1048575) /
>> 1048576))M ($usersz)"
>> +else
>> + echo " User data: $(( (usersz + 1073741823) /
>> 1073741824))G ($usersz)"
>> +fi
>> +check_truncation "$userimg" "$usersz"
>> +dd if="$userimg" of="$outimg" conv=sparse bs=128K seek=$(( pos / (128
>> * 1024) )) \
>> + count=$(( (usersz + 128 * 1024 - 1) / (128 * 1024) )) status=none
>> +pos=$((pos + usersz))
>> +truncate "$outimg" -s $pos
>
> truncate -s $pos "$outimg"
>
>> +
>> +echo ""
>> +echo "Instantiate by appending to the qemu command line:"
>> +echo " -drive file=$outimg,if=none,format=raw,id=emmc-img"
>> +echo " -device emmc,boot-partition-size=$bootsz,rpmb-partition-
>> size=$rpmbsz,drive=emmc-img"
>
Jan
--
Siemens AG, Foundational Technologies
Linux Expert Center