From: Jan Kiszka <jan.kiszka@siemens.com>
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 <jan.kiszka@siemens.com>
---
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 <jan.kiszka@siemens.com>
+#
+# 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
+ 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
+ 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
+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
+
+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"
--
2.51.0
Hi Jan,
On 17/10/25 14:03, Jan Kiszka wrote:
> From: Jan Kiszka <jan.kiszka@siemens.com>
>
> 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 <jan.kiszka@siemens.com>
> ---
> 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 <jan.kiszka@siemens.com>
> +#
> +# 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"?
> + 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/[/[[/?
> + 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 ...
> +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"
On 03.11.25 14:12, Philippe Mathieu-Daudé wrote:
> Hi Jan,
>
> On 17/10/25 14:03, Jan Kiszka wrote:
>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>
>> 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 <jan.kiszka@siemens.com>
>> ---
>> 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 <jan.kiszka@siemens.com>
>> +#
>> +# 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
On 04.11.25 07:30, Jan Kiszka wrote:
> On 03.11.25 14:12, Philippe Mathieu-Daudé wrote:
>> Hi Jan,
>>
>> On 17/10/25 14:03, Jan Kiszka wrote:
>>> From: Jan Kiszka <jan.kiszka@siemens.com>
>>>
>>> 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 <jan.kiszka@siemens.com>
>>> ---
>>> 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 <jan.kiszka@siemens.com>
>>> +#
>>> +# 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?
>
I tried with dash from debian trixie, and there is no issue like yours
visible.
What problem could macOS have here? Will need your help, don't have
anything mac-like around right now.
Jan
--
Siemens AG, Foundational Technologies
Linux Expert Center
On 4/11/25 10:26, Jan Kiszka wrote: > On 04.11.25 07:30, Jan Kiszka wrote: >> On 03.11.25 14:12, Philippe Mathieu-Daudé wrote: >>> Hi Jan, >>> >>> On 17/10/25 14:03, Jan Kiszka wrote: >>>> From: Jan Kiszka <jan.kiszka@siemens.com> >>>> +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? >> > > I tried with dash from debian trixie, and there is no issue like yours > visible. > > What problem could macOS have here? Will need your help, don't have > anything mac-like around right now. Don't worry, we can merge v6 code part, and add the script / doc during the freeze period.
On 04.11.25 13:33, Philippe Mathieu-Daudé wrote: > On 4/11/25 10:26, Jan Kiszka wrote: >> On 04.11.25 07:30, Jan Kiszka wrote: >>> On 03.11.25 14:12, Philippe Mathieu-Daudé wrote: >>>> Hi Jan, >>>> >>>> On 17/10/25 14:03, Jan Kiszka wrote: >>>>> From: Jan Kiszka <jan.kiszka@siemens.com> > > >>>>> +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? >>> >> >> I tried with dash from debian trixie, and there is no issue like yours >> visible. >> >> What problem could macOS have here? Will need your help, don't have >> anything mac-like around right now. > > Don't worry, we can merge v6 code part, and add the script / doc > during the freeze period. I will send out v6 then. It passed local testing, and I addressed some further details. A colleague with a Mac offered to have look tomorrow on this. It is always a pain there regarding the subtle differences... Jan -- Siemens AG, Foundational Technologies Linux Expert Center
On 04.11.25 13:40, Jan Kiszka wrote: > On 04.11.25 13:33, Philippe Mathieu-Daudé wrote: >> On 4/11/25 10:26, Jan Kiszka wrote: >>> On 04.11.25 07:30, Jan Kiszka wrote: >>>> On 03.11.25 14:12, Philippe Mathieu-Daudé wrote: >>>>> Hi Jan, >>>>> >>>>> On 17/10/25 14:03, Jan Kiszka wrote: >>>>>> From: Jan Kiszka <jan.kiszka@siemens.com> >> >> >>>>>> +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? >>>> >>> >>> I tried with dash from debian trixie, and there is no issue like yours >>> visible. >>> >>> What problem could macOS have here? Will need your help, don't have >>> anything mac-like around right now. >> >> Don't worry, we can merge v6 code part, and add the script / doc >> during the freeze period. > > I will send out v6 then. It passed local testing, and I addressed some > further details. > > A colleague with a Mac offered to have look tomorrow on this. It is > always a pain there regarding the subtle differences... > Linux: stat -c format mac (BSD): stat -f format Seems like "wc -c < file" could be a portable alternative to "stat -L -c %s file". Jan -- Siemens AG, Foundational Technologies Linux Expert Center
© 2016 - 2025 Red Hat, Inc.