Raspberry Pi 4 USB dual boot


A stable version of the Raspberry Pi 4 bootloader with USB boot support is now available, that make USB multi boot much easy. This article will show you how to install Raspberry Pi OS (32-bit) Lite and Raspberry Pi OS (64 bit) beta in one USB driver without 3rd tools.

If you don’t want do it yourself, you could try PINN1, the new version seems support USB boot already.

Upgrade firmware

Boot from SD card and upgrade the firmware.

$ sudo apt update
$ sudo apt full-upgrade
$ sudo rpi-eeprom-update -d -f /lib/firmware/raspberrypi/bootloader/stable/pieeprom-2020-06-15.bin
$ sudo reboot

After reboot, check the bootloader version and config to make sure the firmware installed correctly.

$ vcgencmd bootloader_version
Jun 15 2020 14:36:19
version c302dea096cc79f102cec12aeeb51abf392bd781 (release)
timestamp 1592228179
$ vcgencmd bootloader_config
[all]
BOOT_UART=0
WAKE_ON_GPIO=1
POWER_OFF_ON_HALT=0
DHCP_TIMEOUT=45000
DHCP_REQ_TIMEOUT=4000
TFTP_FILE_TIMEOUT=30000
ENABLE_SELF_UPDATE=1
DISABLE_HDMI=0
BOOT_ORDER=0xf41

Partition and format USB storage

We need create 4 partitions for dual boot, more if you want install more OS. All data on this disk will be lost, make sure to backup the data 1st.

  1. this partition is for /boot, FAT filesystem, 256M ~ 1G would be enough.
  2. this partition for Raspberry Pi OS (32-bit) Lite, ext4 filesystem, 8G or more.
  3. this partition for Raspberry Pi OS (64 bit) beta, ext4 filesystem, 8G or more.
  4. this partition is for data/project shared between OS.
$ sudo parted
(parted) select /dev/sda
(parted) mklabel gpt
(parted) mkpart primary fat32 0% 512MB
(parted) mkpart primary ext4 512MB 8GB
(parted) mkpart primary ext4 8GB 16GB
(parted) mkpart primary ext4 16GB 100%
(parted) quit
$ sudo mkfs.vfat -n BOOT -F 32 /dev/sda1
$ sudo mkfs.ext4 -L OS32 /dev/sda2
$ sudo mkfs.ext4 -L OS64 /dev/sda3
$ sudo mkfs.ext4 -L DATA /dev/sda4

create directories for mount point.

$ mkdir ~/{imgboot,imgroot,osboot,osroot,datas}
$ sudo mount -o noatime /dev/sda1 ~/osboot
$ sudo mount -o noatime /dev/sda4 ~/datas

Copy firmware to osboot, for Raspberry Pi 4, only need start4.elf and fixup4.dat.

$ sudo cp /boot/start4.elf ~/osboot/
$ sudo cp /boot/fixup4.dat ~/osboot/

Download the OS images to ~/datas, if you already have the OS images, copy to ~/datas, and unzip it.

$ sudo unzip -d ~/datas ~/datas/2020-05-27-raspios-buster-arm64.zip
$ sudo unzip -d ~/datas ~/datas/2020-05-27-raspios-buster-lite-armhf.zip

Install Raspberry Pi OS (32-bit) Lite.

Use losetup set up OS image as loop device.

$ sudo losetup -fP --show ~/datas/2020-05-27-raspios-buster-lite-armhf.img
/dev/loop0
/dev/loop0 is then name of the loop device, the OS image has multiple partitions, so /dev/loop0p1 will be the 1st partition, AKA boot partition. Mount the boot partition, and copy the files using rsync. Since it’s dual boot, you need copy files to subdirectory because multiple OS will share same boot partition.
$ sudo mount -o noatime /dev/loop0p1 ~/imgboot
$ sudo rsync -axv ~/imgboot/ ~/osboot/os32

Mount the 2nd partition of the OS image, and copy files to /dev/sda2.

$ sudo mount -o noatime /dev/loop0p2 ~/imgroot
$ sudo mount -o noatime /dev/sda2 ~/osroot
$ sudo sudo rsync -axv ~/imgroot/ ~/osroot

Umount the OS image and detach the loop device.

$ sudo umount ~/imgboot
$ sudo umount ~/imgroot
$ sudo losetup -d /dev/loop0

You need tell firmware where to look for the kernel and other files since it’s not in top directory, you can do that by appending os_prefix=os32/ to config.txt**.

$ echo -e "\n[all]\nos_prefix=os32/\n" | sudo tee -a ~/osboot/os32/config.txt

And remove init_resize.sh from cmdline.txt then change the PARTUUID of root.

$ sudo sed -i 's/init=\/usr\/lib\/raspi-config\/init_resize.sh//' ~/osboot/os32/cmdline.txt
$ sudo sed -i "s/\(PARTUUID=\)[[:xdigit:]-]\+/\1$(blkid -o value -s PARTUUID /dev/sda2)/" ~/osboot/os32/cmdline.txt

Since you can’t mount subdirectory directly, you need mount the partition to /mnt/root/boot and bind /mnt/root/boot/os32 to /boot.

$ sudo mkdir -p ~/osroot/mnt/root/boot
$ sudo sed -i "s/\(PARTUUID=\)[[:xdigit:]-]\+\([[:blank:]]*\)\/boot/\1$(blkid -o value -s PARTUUID /dev/sda1)\2\/mnt\/root\/boot/" ~/osroot/etc/fstab
$ sudo sed -i "s/\(PARTUUID=\)[[:xdigit:]-]\+\([[:blank:]]*\/[[:blank:]]\+\)/\1$(blkid -o value -s PARTUUID /dev/sda2)\2/" ~/osroot/etc/fstab
$ echo -e "\n/mnt/root/boot/os32 /boot  none bind 0 0\n" | sudo tee -a ~/osroot/etc/fstab

In case something went wrong, we enable SSH by creating a file called ssh under boot folder, remember to change the password later.

$ sudo touch ~/osboot/os32/ssh

Install Raspberry Pi OS (64 bit) beta

Repeat the process to install Raspberry Pi OS (64 bit) beta.

$ sudo losetup -fP --show ~/datas/2020-05-27-raspios-buster-arm64.img
/dev/loop0
$ sudo mount -o noatime /dev/loop0p1 ~/imgboot
$ sudo rsync -axv ~/imgboot/ ~/osboot/os64
$ sudo mount -o noatime /dev/loop0p2 ~/imgroot
$ sudo mount -o noatime /dev/sda3 ~/osroot
$ sudo sudo rsync -axv ~/imgroot/ ~/osroot
$ sudo umount ~/imgboot
$ sudo umount ~/imgroot
$ sudo losetup -d /dev/loop0
$ echo -e "\n[all]\nos_prefix=os64/\n" | sudo tee -a ~/osboot/os64/config.txt
$ sudo sed -i 's/init=\/usr\/lib\/raspi-config\/init_resize.sh//' ~/osboot/os64/cmdline.txt
$ sudo sed -i "s/\(PARTUUID=\)[[:xdigit:]-]\+/\1$(blkid -o value -s PARTUUID /dev/sda3)/" ~/osboot/os64/cmdline.txt
$ sudo mkdir -p ~/osroot/mnt/root/boot
$ sudo sed -i "s/\(PARTUUID=\)[[:xdigit:]-]\+\([[:blank:]]*\)\/boot/\1$(blkid -o value -s PARTUUID /dev/sda1)\2\/mnt\/root\/boot/" ~/osroot/etc/fstab
$ sudo sed -i "s/\(PARTUUID=\)[[:xdigit:]-]\+\([[:blank:]]*\/[[:blank:]]\+\)/\1$(blkid -o value -s PARTUUID /dev/sda3)\2/" ~/osroot/etc/fstab
$ echo -e "\n/mnt/root/boot/os64 /boot  none bind 0 0\n" | sudo tee -a ~/osroot/etc/fstab
$ sudo touch ~/osboot/os64/ssh

If you want boot into Raspberry Pi OS (32-bit) Lite, run

$ sudo cp ~/osboot/os32/config.txt ~/osboot/config.txt

For Raspberry Pi OS (64 bit) beta, run

$ sudo cp ~/osboot/os64/config.txt ~/osboot/config.txt

Now you can power off the Pi and remove the SD card, after power up, it should boot from USB. Though you have to boot into one OS to switch the OS. Alternatively, you could use GPIO2 to control which OS to boot.

[gpio2=0]
arm_64bit=0
os_prefix=os32/

[gpio2=1]
arm_64bit=1
os_prefix=os64/

Summary

With 4G RAM and USB boot, Raspberry Pi 4 seems good enough as s a desktop PC. It consume less power, and quiet with passive cooling. Is it good enough to run casual game like Terraria? That’s what I want to find out.


  1. PINN - An enhanced version of NOOBS ↩︎

  2. The GPIO filter - Conditional filters in config.txt ↩︎