Dual-Boot Image for x86 platforms

I am developing a dual-boot image config for x86 platforms (e.g. APU2) in the style found in more expensive managed switches. The SD card has 3 partitions, the usual /boot, a rootfs A and a rootfs B.

By default, the system boots from the active image (either A or B), while firmware upgrades always are being written to the backup image (also A or B). A cmdline utility and a LuCI module lets users exchange the image types, so that either the factory default can be retained permanently by always flashing to the same image or the previous firmware version can be stored in the backup image depending of the requirements of the user. It's in a way similar to the dualboot-images in T1/T2/T3 switches from TP-Link and in Ubiquiti's EdgeMax routers.

So far everything works fine, but when booting the B image (3rd partition), LEDE doesn't mount the /boot partition, but if loading the A image (2nd partition), the /boot partition will be mounted below /boot, albeit read-only (why? It should be mounted read/write IMHO).

I tracked this down to the preinit script 79_move_config, which moves the saved config in /boot/sysupgrade.tgz to the root directory, but to be honest, I don't understand the logic in boot_hook_add preinit_mount_root move_config. Which program calls this hook? procd?

Of course, the kernel cmdline is correct for both images as far as I can see.
For image A on partition 2 it reads:

BOOT_IMAGE=/boot/vmlinuz-flashA root=PARTUUID=9a0f04d3-02 rootfstype=ext4 rootwait console=ttyS0,115200n8 noinitrd

For image B on partition 3 it is:

BOOT_IMAGE=/boot/vmlinuz-flashB root=PARTUUID=9a0f04d3-03 rootfstype=ext4 rootwait console=ttyS0,115200n8 noinitrd

Except for the cmdline, both kernels and rootfs partitions are identical.

So my question is: is this intentional that a config can be restored only in the 2nd partition or is this probably a bug?

2 Likes

After one more day of hard testing I finally got it. To whom it may concern: /lib/upgrade/common.sh is buggy.

In function export_bootdevice() the disk partition 2 is hard-coded (!) for whatever reason. Seems no one did test this with another partition.

Lines 226+ of /lib/upgrade/common.sh read:

case "$disk" in
  PARTUUID=[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]-02)
        uuid="${disk#PARTUUID=}"
        uuid="${uuid%-02}"

Correct version without a hard-coded partition number would be:

local uuidpattern
...
uuidpattern='PARTUUID=[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]-[0-9][0-9]'
case $disk in
  $uuidpattern)
        uuid="${disk#PARTUUID=}"
        uuid="${uuid%-*}"

Now it works as intented. The /boot partition is being mounted under the /boot mount-point also for a rootfs on other partitions than only the 2nd.

BTW: If using sysupgrade -p ... the functions export_bootdevice() and export_partdevice() throw errors caused by empty variables being evaluated in test context, which can lead to serious errors such as filesystem destruction, even when booting the first rootfs partition (/dev/mmcblk0p2 or /dev/sda2):

Performing system upgrade...
ash: 7: unknown operand
ash: 179: unknown operand
Upgrade completed
Rebooting system...

...

[    5.575497] EXT4-fs (mmcblk0p2): warning: mounting fs with errors, running e2fsck is recommended
[    5.587621] EXT4-fs (mmcblk0p2): re-mounted. Opts: (null)
[    5.639233] EXT4-fs error (device mmcblk0p2): ext4_mb_generate_buddy:758: group 1, block bitmap and bg descriptor inconsistent: 32236 vs 32237 free clusters
[    5.656429] Aborting journal on device mmcblk0p2-8.
[    5.663503] EXT4-fs (mmcblk0p2): Remounting filesystem read-only
[    5.673225] EXT4-fs error (device mmcblk0p2) in ext4_do_update_inode:4690: Journal has aborted
[    5.684938] EXT4-fs error (device mmcblk0p2) in ext4_evict_inode:246: Journal has aborted
mkdir: can't create directory '/boot': Read-only[    5.699235] urandom-seed: Seed file not found (/etc/urandom.seed)
 file system
mount: mounting /dev/mmcblk0p1 on /boot failed: No such file or di[    5.712426] procd: - early -
rectory

Surprisingly, where double quotes are not needed at all in functions export_bootdevice() / export_partdevice() such as, for example, in

case "$var" in     # 'var' is implicitly quoted between 'case' and 'in'

there are explicit quotes, while in other places where one would write quotes such as, for example, in:

if [ $BOOTDEV_MAJOR = $MAJOR -a $(($BOOTDEV_MINOR + $offset)) = $MINOR -a -b "/dev/$DEVNAME" ]

they are missing.

Complete patch is available by mail at request if you want it.

2 Likes

Sorry to Zombie this thread but this is exactly what I'm looking for! Did this ever make it into the core codebase?

Could I get the patch? Thanks!

might be useful, not automated as OPs though ...