Browse Source
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> Reviewed-by: Philippe Mathieu-Daudé <philmd@linaro.org> Tested-by: Philippe Mathieu-Daudé <philmd@linaro.org> Message-ID: <eecefa8e-44ae-45ff-85d0-3f9b786948e0@siemens.com> Signed-off-by: Philippe Mathieu-Daudé <philmd@linaro.org>pull/307/head
committed by
Philippe Mathieu-Daudé
1 changed files with 216 additions and 0 deletions
@ -0,0 +1,216 @@ |
|||
#!/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> |
|||
# |
|||
|
|||
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=$(wc -c < "$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 |
|||
# check if "$size" is a valid integer by doing a self-comparison |
|||
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=$(wc -c < "$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 -s 0 "$outimg" |
|||
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 -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" |
|||
Loading…
Reference in new issue