#!/bin/sh # # PiLC bootstrap # # Copyright 2016-2024 Michael Büsch # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License along # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. # basedir="$(dirname "$0")" [ "$(echo "$basedir" | cut -c1)" = '/' ] || basedir="$PWD/$basedir" basedir="$(readlink -e "$basedir")" [ -n "$basedir" ] || { echo "Failed to canonicalize base directory." >&2; exit 1; } AWLSIM_MIRROR="https://git.bues.ch/git/awlsim.git" SUITE=bookworm MAIN_MIRROR_32="http://raspbian.raspberrypi.com/raspbian/" MAIN_MIRROR_ARCHIVE="http://archive.raspberrypi.org/debian/" MAIN_MIRROR_64="http://deb.debian.org/debian/" MAIN_MIRROR_64_SECURITY="http://deb.debian.org/debian-security/" KEYRING_VERSION="20120528.2" KEYRING_BASEURL="$MAIN_MIRROR_32/pool/main/r/raspbian-archive-keyring" KEYRING_TGZ_FILE="raspbian-archive-keyring_${KEYRING_VERSION}.tar.gz" KEYRING_TGZ_SHA256="fdf50f775b60901a2783f21a6362e2bf5ee6203983e884940b163faa1293c002" PPL_VERSION="0.1.1" PPL_FILE="ppl_v$PPL_VERSION.zip" PPL_PATH="libs/pixtend/v1/ppl/$PPL_FILE" PPL_SHA256="103edcdbc377f8b478fcbc89117cbad143500c611cb714568f55513cece220d4" PPL2_VERSION="0.1.4" PPL2_FILE="pplv2_v$PPL2_VERSION.zip" PPL2_PATH="libs/pixtend/v2/pplv2/$PPL2_FILE" PPL2_SHA256="4c379e15c6536b67b3eb36b79946a52e57aa79681265e6d46104e07987147bf1" info() { echo "--- $*" } error() { echo "=== ERROR: $*" >&2 } warning() { echo "=== WARNING: $*" >&2 } die() { error "$*" exit 1 } # print the first of its arguments. first() { echo "$1" } # print the last of its arguments. last() { while [ $# -gt 1 ]; do shift; done echo "$1" } # $1=program_name have_program() { which "$1" >/dev/null 2>&1 } # $1=program_name, ($2=description) assert_program() { local bin="$1" local desc="$2" [ -n "$desc" ] || desc="$bin" have_program "$bin" || die "$bin not found. Please install $desc." } term_signal() { die "Terminating signal received" } cleanup() { info "Cleaning up..." for mp in "$mp_shm" "$mp_proc_binfmt_misc" "$mp_proc" "$mp_sys" "$mp_bootimgfile" "$mp_rootimgfile"; do [ -n "$mp" -a -d "$mp" ] &&\ umount -l "$mp" >/dev/null 2>&1 done for mp in "$mp_bootimgfile" "$mp_rootimgfile"; do [ -n "$mp" -a -d "$mp" ] &&\ rmdir "$mp" >/dev/null 2>&1 done } do_install() { install "$@" || die "Failed install $*" } do_systemctl() { info "systemctl $*" systemctl "$@" || die "Failed to systemctl $*" } write_image() { local image="$1" local dev="$2" [ -b "$dev" ] || die "$dev is not a block device" mount | grep -q "$dev" && die "$dev is mounted. Refusing to write to it!" if have_program blkdiscard; then info "Discarding $dev ..." blkdiscard -f "$dev" ||\ error "blkdiscard failed." else warning "Skipping discard. blkdiscard not installed." fi info "Writing $image to $dev ..." dd if="$image" of="$dev" bs=32M status=progress ||\ die "Failed to write image." } download() { local target="$1" local mirror="$2" local sha256="$3" info "Downloading $mirror..." rm -f "$target" if printf '%s' "$mirror" | grep -qEe '^\./|^\../|^/'; then # "mirror" starts with ./ ../ or / # This is a local file. cp "$mirror" "$target" || die "Failed to fetch $mirror" else # Download the file wget -O "$target" "$mirror" || die "Failed to fetch $mirror" fi [ "$(sha256sum -b "$target" | cut -f1 -d' ')" = "$sha256" ] ||\ die "SHA256 verification of $target failed" } extract_archive() { local archive="$1" local extract_dir="$2" local make_extract_dir="$3" if [ $make_extract_dir -ne 0 ]; then mkdir "$extract_dir" ||\ die "Failed to create directory $extract_dir" fi if printf '%s' "$archive" | grep -qEe '\.zip$'; then if [ $make_extract_dir -ne 0 ]; then unzip -d "$extract_dir" "$archive" ||\ die "Failed to unpack $archive" else unzip "$archive" ||\ die "Failed to unpack $archive" fi else if [ $make_extract_dir -ne 0 ]; then tar --one-top-level="$extract_dir" -xf "$archive" ||\ die "Failed to unpack $archive" else tar -xf "$archive" ||\ die "Failed to unpack $archive" fi fi } build_pythonpack() { local python="$1" local name="$2" local archive="$3" local extract_dir="$4" local make_extract_dir="$5" info "Building $name for $python..." rm -rf "/tmp/$extract_dir" ( cd /tmp || die "Failed to cd /tmp" extract_archive "$archive" "$extract_dir" "$make_extract_dir" cd "$extract_dir" ||\ die "Failed to cd $extract_dir" "$python" ./setup.py install ||\ die "Failed to install $name" ) || die rm -r "/tmp/$extract_dir" ||\ die "Failed to remove $name build files." } build_ppl() { local archive="$PPL_FILE" for python in python3; do build_pythonpack "$python" "ppl-$PPL_VERSION" \ "$archive" "ppl-$PPL_VERSION" 1 done rm "/tmp/$archive" ||\ die "Failed to remove /tmp/$archive." } build_ppl2() { local archive="$PPL2_FILE" for python in python3; do build_pythonpack "$python" "ppl2-$PPL2_VERSION" \ "$archive" "ppl2-$PPL2_VERSION" 1 done rm "/tmp/$archive" ||\ die "Failed to remove /tmp/$archive." } pilc_bootstrap_first_stage() { echo "Running first stage..." [ "$(id -u)" = "0" ] || die "Permission denied. Must be root." # Check host tools (first/third stage). if have_program 7zz; then SEVENZIP=7zz elif have_program 7z; then SEVENZIP=7z else die "7z not found. Please install 7z." fi assert_program chroot assert_program dd assert_program debootstrap assert_program git assert_program gpg assert_program install assert_program mkfs.ext4 assert_program mkfs.vfat assert_program parted assert_program rsync assert_program setarch assert_program tar assert_program unzip assert_program wget [ -x "$opt_qemu" ] ||\ die "The qemu binary '$opt_qemu' is not executable." info "Cleaning tmp..." rm -rf "$opt_target_dir"/tmp/* do_install -d -o root -g root -m 1777 "$opt_target_dir/tmp" info "Downloading and extracting keys..." do_install -o root -g root -m 644 \ "$basedir/keys/CF8A1AF502A2AA2D763BAE7E82B129927FA3303E.gpg" \ "$opt_target_dir/tmp/" if [ $opt_bit -eq 32 ]; then download "$opt_target_dir/tmp/$KEYRING_TGZ_FILE" \ "$KEYRING_BASEURL/$KEYRING_TGZ_FILE" \ "$KEYRING_TGZ_SHA256" tar -C "$opt_target_dir/tmp" -x -f "$opt_target_dir/tmp/$KEYRING_TGZ_FILE" ||\ die "Failed to extract keys." local raspbian_asc="$opt_target_dir/tmp/raspbian-archive-keyring-$KEYRING_VERSION/raspbian.public.key" local raspbian_gpg="$raspbian_asc.gpg" gpg --dearmor < "$raspbian_asc" > "$raspbian_gpg" ||\ die "Failed to convert key." fi # debootstrap first stage. if [ $opt_skip_debootstrap1 -eq 0 ]; then info "Running debootstrap first stage..." if [ $opt_bit -eq 32 ]; then local arch="armhf" local keyopt="--keyring=$raspbian_gpg" local mirror="$MAIN_MIRROR_32" else local arch="arm64" local keyopt= local mirror="$MAIN_MIRROR_64" fi setarch "linux$opt_bit" \ debootstrap --arch="$arch" --foreign \ --components="main,contrib,non-free" \ $keyopt \ "$SUITE" "$opt_target_dir" "$mirror" \ || die "debootstrap failed" fi [ -d "$opt_target_dir" ] ||\ die "Target directory '$opt_target_dir' does not exist." # Avoid the start of daemons during second stage. do_install -o root -g root -m 755 \ "$basedir/templates/policy-rc.d" \ "$opt_target_dir/usr/sbin/" # Copy qemu. info "Copying qemu binary from '$opt_qemu' into '$opt_target_dir/usr/bin/'..." do_install -o root -g root -m 755 "$opt_qemu" "$opt_target_dir/usr/bin" info "Copying PiLC bootstrap script and templates..." do_install -o root -g root -m 755 \ "$basedir/pilc-bootstrap.sh" \ "$opt_target_dir/" cp -r "$basedir/templates" "$opt_target_dir/tmp/" ||\ die "Failed to copy PiLC templates" cp -r "$basedir/deb" "$opt_target_dir/tmp/" ||\ die "Failed to copy PiLC deb packages" info "Checking out awlsim..." local awlsim_dir="$opt_target_dir/tmp/awlsim" local awlsim_checkout_dir="$awlsim_dir/src" rm -rf "$awlsim_dir" do_install -d -o root -g root -m 755 "$awlsim_dir" git clone --no-checkout "$AWLSIM_MIRROR" "$awlsim_checkout_dir" ||\ die "Failed to clone" ( cd "$awlsim_checkout_dir" ||\ die "Failed to cd" git checkout "$opt_branch" ||\ die "Failed to check out branch." git submodule update --init --recursive submodules/pyprofibus ||\ die "Failed to pull pyprofibus submodule" rm -r \ .git \ submodules/pyprofibus/.git \ submodules/pyprofibus/phy_fpga/crcgen/.git ||\ die "Failed to remove .git directory." mv submodules/pyprofibus .. ||\ die "Failed to move pyprofibus submodule." ) || die # Fetch packages download "$opt_target_dir/tmp/$PPL_FILE" \ "$awlsim_checkout_dir/$PPL_PATH" \ "$PPL_SHA256" download "$opt_target_dir/tmp/$PPL2_FILE" \ "$awlsim_checkout_dir/$PPL2_PATH" \ "$PPL2_SHA256" # Second stage will mount a few filesystems. # Keep track to umount them in cleanup. mp_proc="$opt_target_dir/proc" mp_proc_binfmt_misc="$opt_target_dir/proc/sys/fs/binfmt_misc" mp_sys="$opt_target_dir/sys" mp_shm="$opt_target_dir/dev/shm" } pilc_bootstrap_second_stage() { info "Running second stage..." [ -x /pilc-bootstrap.sh ] ||\ die "Second stage does not contain the bootstrap script." # Set up environment. export LC_ALL=C export LANGUAGE=C export LANG=C if [ "$opt_rpiver" = "1" -o "$opt_rpiver" = "0" ]; then info "Optimizing for RPi 1, zero(w)" local march="-march=armv6kz" local mtune="-mtune=arm1176jzf-s" elif [ "$opt_rpiver" = "2" ]; then info "Optimizing for RPi 2" local march="-march=armv7-a" local mtune="-mtune=cortex-a7" elif [ "$opt_rpiver" = "3" ]; then info "Optimizing for RPi 3" local march="-march=armv8-a" local mtune="-mtune=cortex-a53" elif [ "$opt_rpiver" = "4" ]; then info "Optimizing for RPi 4" local march="-march=armv8-a" local mtune="-mtune=cortex-a72" else info "Optimizing for generic RPi" if [ $opt_bit -eq 32 ]; then local march="-march=armv6kz" local mtune= else local march="-march=armv8-a" local mtune= fi fi export CFLAGS="-O3 $march $mtune -pipe" [ $opt_bit -eq 32 ] && export CFLAGS="$CFLAGS -mfpu=vfp -mfloat-abi=hard" export CXXFLAGS="$CFLAGS" # debootstrap second stage. if [ $opt_skip_debootstrap2 -eq 0 ]; then info "Running debootstrap second stage..." /debootstrap/debootstrap --verbose --second-stage ||\ die "Debootstrap second stage failed." fi if [ $opt_bit -eq 32 ]; then info "Disabling raspi-copies-and-fills..." rm -f /etc/ld.so.preload || die "Failed to disable raspi-copies-and-fills" fi info "Mounting /proc..." do_install -d -o root -g root -m 755 /proc mount -t proc proc /proc || die "Mounting /proc failed." info "Mounting /sys..." do_install -d -o root -g root -m 755 /sys mount -t sysfs sysfs /sys || die "Mounting /sys failed." info "Mounting /dev/shm..." do_install -d -o root -g root -m 755 /dev/shm mount -t tmpfs tmpfs /dev/shm || die "Mounting /dev/shm failed." info "Creating /etc/fstab" do_install -d -o root -g root -m 755 /config do_install -T -o root -g root -m 644 \ /tmp/templates/fstab \ /etc/fstab info "Writing misc /etc stuff..." echo "pilc" > /etc/hostname || die "Failed to set hostname" printf 'PiLC GNU/Linux (based on Raspberry Pi OS) \\n \\l\n\n' > /etc/issue ||\ die "Failed to create /etc/issue" printf 'PiLC GNU/Linux (based on Raspberry Pi OS)\n' > /etc/issue.net ||\ die "Failed to create /etc/issue.net" sed -i -e 's|PRETTY_NAME=.*|PRETTY_NAME="PiLC"|' \ /etc/os-release ||\ die "Failed to set os-release PRETTY_NAME." sed -i -e 's|NAME=.*|NAME="PiLC"|' \ /etc/os-release ||\ die "Failed to set os-release NAME." sed -i -e 's|ID=.*|ID=pilc|' \ /etc/os-release ||\ die "Failed to set os-release ID." sed -i -e 's|ID_LIKE=.*|ID_LIKE=debian|' \ /etc/os-release ||\ die "Failed to set os-release ID_LIKE." sed -i -e 's|HOME_URL=.*|HOME_URL="https://bues.ch/a/pilc"|' \ /etc/os-release ||\ die "Failed to set os-release HOME_URL." sed -i -e 's|SUPPORT_URL=.*|SUPPORT_URL="https://bues.ch/a/pilc"|' \ /etc/os-release ||\ die "Failed to set os-release SUPPORT_URL." sed -i -e 's|BUG_REPORT_URL=.*|BUG_REPORT_URL="https://bues.ch/a/pilc"|' \ /etc/os-release ||\ die "Failed to set os-release BUG_REPORT_URL." info "Writing apt configuration..." local apt_opts="-y -o Acquire::Retries=3" if [ $opt_bit -eq 32 ]; then cat > /etc/apt/sources.list < /etc/apt/sources.list < /etc/apt/apt.conf.d/99no-translations ||\ die "Failed to set apt.conf.d" cat /tmp/templates/debconf-set-selections-preinstall.conf | debconf-set-selections ||\ die "Failed to configure debconf settings" apt-get $apt_opts update ||\ die "apt-get update failed" apt-get $apt_opts install apt-transport-https ||\ die "apt-get install apt-transport-https failed" apt-get $apt_opts install \ gnupg2 \ debian-keyring \ || die "apt-get install keyrings failed" cat > /etc/apt/sources.list.d/raspi.list <> /etc/hosts ||\ die "Failed to update /etc/hosts" fi info "Building Python modules..." build_ppl build_ppl2 info "Building awlsim..." ( cd /tmp/awlsim/src || die "Failed to cd" if [ $opt_cython -eq 0 ]; then # Disable cython sed -i -e '/Package: cython/,/^$/ d' \ debian/control ||\ die "Failed to patch control file (cython)" sed -i -e 's/export AWLSIM_CYTHON_BUILD=1/export AWLSIM_CYTHON_BUILD=0/' \ debian/rules ||\ die "Failed to patch rules file (cython)" fi # Update the systemd service file. sed -i -e 's|AWLSIM_SCHED=|AWLSIM_SCHED=realtime-if-multicore|g' \ -e 's|AWLSIM_PRIO=|AWLSIM_PRIO=50|g' \ -e 's|AWLSIM_AFFINITY=|AWLSIM_AFFINITY=-1,-2,-3|g' \ -e 's|AWLSIM_MLOCK=|AWLSIM_MLOCK=1|g' \ -e 's|Nice=.*$|Nice=-5|g' \ awlsim-server.service ||\ die "Failed to patch awlsim-server.service" # Build the packages. debuild -uc -us -b -d || die "debuild failed" info "Installing awlsim..." # Core dpkg -i ../python3-awlsim_*.deb ||\ die "Failed to install python3-awlsim" if [ $opt_cython -ne 0 ]; then dpkg -i ../cython3-awlsim_*.deb ||\ die "Failed to install cython3-awlsim" fi # hardware: dummy dpkg -i ../python3-awlsimhw-dummy_*.deb ||\ die "Failed to install python3-awlsimhw-dummy" if [ $opt_cython -ne 0 ]; then dpkg -i ../cython3-awlsimhw-dummy_*.deb ||\ die "Failed to install cython3-awlsimhw-dummy" fi # hardware: linuxcnc dpkg -i ../python3-awlsimhw-linuxcnc_*.deb ||\ die "Failed to install python3-awlsimhw-linuxcnc" if [ $opt_cython -ne 0 ]; then dpkg -i ../cython3-awlsimhw-linuxcnc_*.deb ||\ die "Failed to install cython3-awlsimhw-linuxcnc" fi # hardware: profibus dpkg -i ../python3-awlsimhw-profibus_*.deb ||\ die "Failed to install python3-awlsimhw-profibus" if [ $opt_cython -ne 0 ]; then dpkg -i ../cython3-awlsimhw-profibus_*.deb ||\ die "Failed to install cython3-awlsimhw-profibus" fi # hardware: RPi GPIO dpkg -i ../python3-awlsimhw-rpigpio_*.deb ||\ die "Failed to install python3-awlsimhw-rpigpio" if [ $opt_cython -ne 0 ]; then dpkg -i ../cython3-awlsimhw-rpigpio_*.deb ||\ die "Failed to install cython3-awlsimhw-rpigpio" fi # hardware: PiXtend dpkg -i ../python3-awlsimhw-pixtend_*.deb ||\ die "Failed to install python3-awlsimhw-pixtend" if [ $opt_cython -ne 0 ]; then dpkg -i ../cython3-awlsimhw-pixtend_*.deb ||\ die "Failed to install cython3-awlsimhw-pixtend" fi # Executables dpkg -i ../awlsim-server_*.deb ||\ die "Failed to install awlsim-server" dpkg -i ../awlsim-client_*.deb ||\ die "Failed to install awlsim-client" dpkg -i ../awlsim-symtab_*.deb ||\ die "Failed to install awlsim-symtab" dpkg -i ../awlsim-test_*.deb ||\ die "Failed to install awlsim-test" dpkg -i ../awlsim-proupgrade_*.deb ||\ die "Failed to install awlsim-proupgrade" # Copy debs rm -rf /home/pi/deb/awlsim do_install -d -o pi -g pi -m 755 /home/pi/deb/awlsim do_install -o pi -g pi -m 644 \ ../*awlsim*.deb \ /home/pi/deb/awlsim/ # Copy examples do_install -T -o pi -g pi -m 644 \ examples/EXAMPLE.awlpro \ /home/pi/generic-example.awlpro do_install -T -o pi -g pi -m 644 \ examples/raspberrypi-gpio.awlpro \ /home/pi/raspberrypi-gpio-example.awlpro do_install -T -o pi -g pi -m 644 \ examples/raspberrypi-profibus.awlpro \ /home/pi/raspberrypi-profibus-example.awlpro do_install -T -o pi -g pi -m 644 \ examples/raspberrypi-pixtend.awlpro \ /home/pi/raspberrypi-pixtend-example.awlpro ) || die info "Building pyprofibus..." ( cd /tmp/awlsim/pyprofibus ||\ die "Failed to cd" if [ $opt_cython -eq 0 ]; then # Disable cython sed -i -e '/Package: cython/,/^$/ d' \ debian/control ||\ die "Failed to patch control file (cython)" sed -i -e 's/export PYPROFIBUS_CYTHON_BUILD=1/export PYPROFIBUS_CYTHON_BUILD=0/' \ debian/rules ||\ die "Failed to patch rules file (cython)" fi # Build the packages. debuild -uc -us -b -d || die "debuild failed" info "Installing pyprofibus..." dpkg -i ../python3-pyprofibus_*.deb ||\ die "Failed to install python3-pyprofibus" dpkg -i ../profisniff_*.deb ||\ die "Failed to install profisniff" dpkg -i ../gsdparser_*.deb ||\ die "Failed to install gsdparser" # Copy debs rm -rf /home/pi/deb/pyprofibus do_install -d -o pi -g pi -m 755 /home/pi/deb/pyprofibus do_install -o pi -g pi -m 644 \ ../*pyprofibus*.deb \ ../profisniff_*.deb \ ../gsdparser_*.deb \ /home/pi/deb/pyprofibus/ ) || die rm -r /tmp/awlsim ||\ die "Failed to remove awlsim checkout." info "Updating home directory permissions..." chown -R pi:pi /home/pi || die "Failed to change /home/pi permissions." # Remove rc.d policy file if [ -e /usr/sbin/policy-rc.d ]; then rm /usr/sbin/policy-rc.d ||\ die "Failed to remove policy-rc.d" fi if [ $opt_bit -eq 32 ]; then # Install this last. It won't work correctly in the qemu environment. info "Installing raspi-copies-and-fills..." apt-get $apt_opts install --reinstall raspi-copies-and-fills ||\ die "apt-get install failed" fi info "Stopping processes..." for i in dbus ssh irqbalance; do /etc/init.d/$i stop done } pilc_bootstrap_third_stage() { info "Running third stage..." info "Umounting /dev/shm..." umount -l "$mp_shm" || die "Failed to umount /dev/shm" info "Umounting /sys..." umount -l "$mp_sys" || die "Failed to umount /sys" info "Umounting /proc/sys/fs/binfmt_misc..." umount -l "$mp_proc_binfmt_misc" info "Umounting /proc..." umount -l "$mp_proc" || die "Failed to umount /proc" info "Removing PiLC bootstrap script..." rm "$opt_target_dir/pilc-bootstrap.sh" ||\ die "Failed to remove bootstrap script." info "Cleaning tmp..." rm -rf "$opt_target_dir"/tmp/* info "Configuring /boot/firmware..." do_install -T -o root -g root -m 644 \ "$basedir/templates/boot_cmdline.txt" \ "$opt_target_dir/boot/firmware/cmdline.txt" do_install -T -o root -g root -m 644 \ "$basedir/templates/boot_config.txt" \ "$opt_target_dir/boot/firmware/config.txt" ln -sf firmware/cmdline.txt "$opt_target_dir/boot/cmdline.txt" ||\ die "Failed to create cmdline.txt link." ln -sf firmware/config.txt "$opt_target_dir/boot/config.txt" ||\ die "Failed to create config.txt link." if [ $opt_bit -eq 64 ]; then sed -i -e 's/arm_64bit=0/arm_64bit=1/g' \ "$opt_target_dir/boot/firmware/config.txt" ||\ die "Failed to set arm_64bit=1" fi # Prepare image paths. local imgfile="${opt_target_dir}${opt_imgsuffix}.img" local imgfile_zip="${imgfile}.7z" local firmwareimgfile="${imgfile}.firmware" mp_firmwareimgfile="${firmwareimgfile}.mp" local rootimgfile="${imgfile}.root" mp_rootimgfile="${rootimgfile}.mp" rm -f "$imgfile" "$imgfile_zip" "$rootimgfile" "$firmwareimgfile" rmdir "$mp_firmwareimgfile" "$mp_rootimgfile" 2>/dev/null # Create images. if [ "$opt_img" -ne 0 ]; then # Calculate image size. local imgsize_b="$(expr "$opt_imgsize" \* 1000 \* 1000 \* 1000)" local imgsize_mib="$(expr "$imgsize_b" \/ 1024 \/ 1024)" # Reduce the size to make sure it fits every SD card. local imgsize_mib_red="$(expr \( "$imgsize_mib" \* 98 \) \/ 100)" [ -n "$imgsize_mib_red" ] || die "Failed to calculate image size" info "SD image size = $imgsize_mib_red MiB" info "Creating /boot/firmware image..." mkfs.vfat -F 32 -i 7771B0BB -n boot -C "$firmwareimgfile" \ $(expr \( 256 \* 1024 \) ) ||\ die "Failed to create /boot/firmware partition file system." mkdir "$mp_firmwareimgfile" ||\ die "Failed to make /boot/firmware partition mount point." mount -o loop "$firmwareimgfile" "$mp_firmwareimgfile" ||\ die "Failed to mount /boot/firmware partition." rsync -rt --inplace \ "$opt_target_dir/boot/firmware/" "$mp_firmwareimgfile/" ||\ die "Failed to copy /boot/firmware files." umount "$mp_firmwareimgfile" ||\ die "Failed to umount /boot/firmware partition." rmdir "$mp_firmwareimgfile" ||\ die "Failed to remove /boot/firmware partition mount point." info "Creating root image..." mkfs.ext4 -O ^metadata_csum_seed \ "$rootimgfile" \ $(expr \( "$imgsize_mib_red" - \( 256 + 4 + 4 \) \) \* 1024 ) ||\ die "Failed to create root filesystem." mkdir "$mp_rootimgfile" ||\ die "Failed to make root partition mount point." mount -o loop "$rootimgfile" "$mp_rootimgfile" ||\ die "Failed to mount root partition." rsync -aHAX --inplace \ --exclude='boot/firmware/*' \ --exclude='dev/shm/*' \ --exclude='proc/*' \ --exclude='run/*' \ --exclude='sys/*' \ --exclude='tmp/*' \ --exclude="$(basename "$opt_qemu")" \ "$opt_target_dir/" "$mp_rootimgfile/" ||\ die "Failed to copy root files." umount "$mp_rootimgfile" ||\ die "Failed to umount root partition." rmdir "$mp_rootimgfile" ||\ die "Failed to remove root partition mount point." info "Creating image '$imgfile'..." dd if=/dev/zero of="$imgfile" bs=1M count="$imgsize_mib_red" conv=sparse ||\ die "Failed to create image file." parted "$imgfile" <