Howto migrate an I2SE EVAcharge SE with Debian-based Firmware to a Yocto-based Firmware

Introduction

I2SE (or now chargebyte) used to ship these devices with a simple Debian-based example firmware installed in the eMMC. These devices used the imx-bootlets project as bootloader. The eMMC consists only of the bootloader partition and one big root filesystem.

Later in time, I2SE used the RAUC project for there firmware updates. For this, they switched to a A/B system setup and changed the partitioning of the eMMC accordingly. The bootloader was also switched to U-Boot to support the switching between the A and B system. This concept was also kept when switching to Yocto-based BSPs.

Upgrading from an older Debian-based firmware to an Yocto-based firmware is not trivial when it should be done in the field. The reason is that you have to change the partitioning on-the-fly.

The following instructions demonstrates that such an update is technically possible and give you the files as example at hand.

Disclaimer: While the author of this document tested the whole procedure, there is no guarantee that it works flawlessly or applies to all real-world setups. There is a risk that you brick your devices and that it is not reachable again over network. Depending on what went wrong in this case, a technician might be required at the charger to physically replace the board.

Concept

The main problem is that you cannot re-partition the eMMC while it is in use by the Linux kernel. The solution is to use a tiny RAM-based Linux system which solely operates during the hot phase of the migration. The main idea used here is, that the “old” system is prepared, then runs the migration and finally boots into the fully updated system running Yocto.

During the preparation phase of the old system, at least three files have to be downloaded to the old root filesystem:

The install procedure provides two hooks for your customizations: one hook is available before the actual migration was started, e.g. to save some configuration data, one hook is available after the eMMC was updated.

The RAM-based Linux configures the network interface for DHCP. Also an SSH server is started – without any password set. That means that during the migration any user on the network could log into the system as user root.

Hands-On

It is out of scope of this document, how the files are transferred to the old system. You need at least:

You have to place these two files first in the correct location before installing the migration firmware. Otherwise, the migration firmware would already boot-up in case of sudden power-losses and finds nothing to use.

The migration firmware: mmcblk0p1.img
This file must be written to the old bootloader partition. In contains the described RAM-based Linux system. Use e.g. the following command: dd of=/dev/mmcblk0p1 if=mmcblk0p1.img

Optional files can be placed in the root filesystem:

For your information, here is the actual migration shell script used by the firmware:

#!/bin/sh

# Assumptions:
# The image of the new target rootfs is gz compressed and
# stored in the root directory (/) of the old rootfs (/dev/mmcblk0p2)
# with a filename ending in '*.rootfs.ext4.gz'.
# The boot loader U-Boot is stored at the same location
# with filename ending in '*.sb'

# exit on errors
set -e

# fs check is required before resize
e2fsck -y -f /dev/mmcblk0p2

# shrink current filesystem so that it fits smaller than final two rootfs
resize2fs -f /dev/mmcblk0p2 2032M || true

# re-partition first round - create future /srv partition already at its final location
cat <<EOF | sfdisk /dev/mmcblk0
label: dos
label-id: 0x5452574f
device: /dev/mmcblk0
unit: sectors

/dev/mmcblk0p1 : start=        2048, size=       16384, type=53, bootable
/dev/mmcblk0p2 : start=       20480, size=     4161536, type=83
/dev/mmcblk0p3 : start=     4212736, size=     2097152, type=83
EOF

# format /srv
mkfs.ext4 -F -F /dev/mmcblk0p3

# mount old rootfs and /srv
mount /dev/mmcblk0p2 /mnt
mkdir -p /srv
mount /dev/mmcblk0p3 /srv

# save file we still need from old rootfs
cp /mnt/*.sb /srv
cp /mnt/*.rootfs.ext4.gz /srv

# run pre-install hook if present
[ -x /mnt/install-pre-hook.sh ] && . /mnt/install-pre-hook.sh

# umount again - partition number will change
umount /mnt /srv

# re-partition 2nd time - final partition layout
cat <<EOF | sfdisk /dev/mmcblk0
label: dos
label-id: 0x6d686569
device: /dev/mmcblk0
unit: sectors

/dev/mmcblk0p1 : start=        2048, size=       14336, type=53
/dev/mmcblk0p2 : start=       16384, size=     2097152, type=83
/dev/mmcblk0p3 : start=     2113536, size=     2097152, type=83
/dev/mmcblk0p4 : start=     4210688, size=     2627584, type=5
/dev/mmcblk0p5 : start=     4212736, size=     2097152, type=83
/dev/mmcblk0p6 : start=     6311936, size=      262144, type=83
/dev/mmcblk0p7 : start=     6576128, size=      262144, type=83
EOF

# mount /srv again
mount /dev/mmcblk0p5 /srv

if [ -z "$SKIP_ROOTFS2" ]; then
    # write rootfs partition 2
    zcat /srv/*.rootfs.ext4.gz | dd of=/dev/mmcblk0p3 bs=4M
    resize2fs /dev/mmcblk0p3
fi

# write rootfs partition 1
zcat /srv/*.rootfs.ext4.gz | dd of=/dev/mmcblk0p2 bs=4M
resize2fs /dev/mmcblk0p2

# cleanup U-Boot environment
dd if=/dev/zero of=/dev/mmcblk0 bs=1k count=128 seek=128
dd if=/dev/zero of=/dev/mmcblk0 bs=1k count=128 seek=256

# write U-Boot
sdimage -f /srv/*.sb -d /dev/mmcblk0

# cleanup & reboot
umount /srv
sync

# run pre-install hook if present
[ -x /mnt/install-post-hook.sh ] && . /mnt/install-post-hook.sh

reboot

As you can see, if something goes wrong, the script terminates and the system is remains in this state – it is reachable via SSH until the next power cycle. After a power cycle, it depends whether the new bootloader was already installed or whether the migration firmware is still in place.

There is also a small weakness of the whole procedure: a power outage during the bootloader installation will most probably brick the system. This is why it is done last. In case a power outage happens before, then the whole re-flashing procedure is simply restarted from scratch.

More History - Yet Another Partitioning Scheme

As found out later, there exists an even older partitioning scheme consisting of 4 partitions: a FAT partition, the bootstream partition, the root filesystem and yet another ext4 partition.

Also these devices can be migrated to the new Yocto approach. Here the advantage is that the rootfs partition must not be reduced in size during the conversion. However, the bootstream partition is in the way and thus moved so that the system should survive a potential power outage during the migration process.

The migration script was adapted accordingly, the latest version can be found on Github, see below. But while the tiny migration Linux system is in core the same, the partition image is slightly different due to other offsets in the bootstream headers.

So you have to use the following migration firmware: mmcblk0p2.img

The installation and remaining features are the same, just replace the content of the second partition with the provided file.

Important Notes

Sources

You can create the tiny Linux system by using the reference repository as a base: https://github.com/mhei/stib-to-yocto-migration-linux.

Last update: 2025-01-10, mhei