I was planning to teach a class on Raspberry Pis, and I wanted to start with the standard Raspbian image, update it, and add some programs like GIMP that we'd use in the class. And I wanted to do that just once, before burning the image to a bunch of different SD cards. Is there a way to do that on my regular Linux box, with its nice fast processor and disk?
Why yes, there is, and it's pretty easy. But there are a lot of unclear or misleading tutorials out there, so I hope this is a bit simpler and easier to follow.
I got most of this from a tutorial that no longer seems to be available (but I'll include the link in case it comes back): Solar Staker/RPI Image/Creation.
I've tested this on Ubuntu 19.10, Debian Stretch and Buster; the instructions should be pretty general except for the name of the loopback mount. Commands you type are in bold; the rest is the output you should see. $ is your normal shell prompt and # is a root prompt.
You'll need kpartx, qemu and some supporting packages. On Debian or Ubuntu:
$ sudo apt install kpartx qemu binfmt-support qemu-user-static
You've probably already downloaded a Raspbian SD card image.
Set Up the Loopback Devices
kpartx can read the Raspbian ISO image and split it into the two filesystems it would have if you wrote it to an SD card. It turns the filesystems into loopback devices you can mount like regular filesystems.
$ sudo kpartx -av 2019-09-26-raspbian-buster-lite.img add map loop10p1 (253:1): 0 524288 linear 7:10 8192 add map loop10p2 (253:2): 0 3858432 linear 7:10 532480
Make a note of those loopback device names. They may not always be loop10p1 and loop10p2, but they'll probably always end in 1 and 2. 1 is the Raspbian /boot filesystem, 2 is the Raspbian root.
(Optional) Check and Possibly Resize the Raspbian Filesystem
Make sure that the Raspbian filesystem is intact, and that it's a reasonable size.
In practice, I didn't find this made any difference (everything was fine to begin with), but it doesn't hurt to make sure.
$ sudo e2fsck -f /dev/mapper/loop10p2 e2fsck 1.45.3 (14-Jul-2019) Pass 1: Checking inodes, blocks, and sizes Pass 2: Checking directory structure Pass 3: Checking directory connectivity Pass 4: Checking reference counts Pass 5: Checking group summary information rootfs: 44367/120720 files (0.3$ non-contiguous), 292014/482304 blocks $ sudo resize2fs /dev/mapper/loop10p2 resize2fs 1.45.3 (14-Jul-2019) The filesystem is already 482304 (4k) blocks long. Nothing to do!
Mount the Loopback Filesystems
You're ready to mount the two filesystems. A Raspbian SD card image contains two filesystems.
Partition 1 is a small vfat /boot filesystem containing the kernel and some other files it needs for booting, plus the two important configuration files cmdline.txt and config.txt.
Partition 2 is the Raspbian root filesystem in ext4 format. The Raspbian root includes an empty /boot directory; mount the root first, then mount the Raspbian boot partition on Raspbian's /boot:
$ sudo mkdir /mnt/pi_image $ sudo mount /dev/mapper/loop10p2 /mnt/pi_image $ sudo mount /dev/mapper/loop10p1 /mnt/pi_image/boot
Prepare for Chroot
You're going to chroot to the Raspbian filesystem. Chroot limits the filesystem you can access, so when you type /, instead of your host filesystem's / you'll see the root of the Raspbian filesystem, /mnt/pi_image. That means you won't have access to your host system's /usr/bin, any more.
But qemu needs /usr/bin/qemu-arm-static to be able to emulate ARM binaries in user mode. So copy that to the Raspbian filesystem so you'll still be able to access it after the chroot:
$ sudo cp /usr/bin/qemu-arm-static /mnt/pi_image/usr/bin
Chroot to the Raspbian System
$ sudo chroot /mnt/pi_image /bin/bash root:/#
Now you're running in Raspbian's root filesystem. All the binaries in your path (e.g. /bin/ls, /bin/bash) are ARM binaries, but if you try to run them, qemu will see qemu-arm-static and run the program as though you're on an actual Raspberry Pi.
Now you can run Raspbian commands.
# file /bin/ls /bin/ls: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=67a394390830ea3ab4e83b5811c66fea9784ee69, stripped # # /bin/ls bin dev home lost+found mnt proc run srv tmp var boot etc lib media opt root sbin sys usr # file /bin/cat /bin/cat: ELF 32-bit LSB executable, ARM, EABI5 version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-armhf.so.3, for GNU/Linux 3.2.0, BuildID[sha1]=2239a192f2f277bd1a4892e39a41eba97266b91f, stripped # # cat /etc/issue Raspbian GNU/Linux 10 \n \l
You can even install packages or update the whole system:
# apt update Get:1 http://raspbian.raspberrypi.org/raspbian buster InRelease [15.0 kB] Get:2 http://archive.raspberrypi.org/debian buster InRelease [25.2 kB] Get:3 http://raspbian.raspberrypi.org/raspbian buster/main armhf Packages [13.0 MB] Get:4 http://archive.raspberrypi.org/debian buster/main armhf Packages [259 kB] Fetched 13.3 MB in 20s (652 kB/s) Reading package lists... Done # apt dist-upgrade Reading package lists... Done Building dependency tree Reading state information... Done Calculating upgrade... Done The following NEW packages will be installed: busybox initramfs-tools initramfs-tools-core klibc-utils libklibc linux-base pigz The following packages will be upgraded: dhcpcd5 e2fsprogs file firmware-atheros firmware-brcm80211 firmware-libertas firmware-misc-nonfree firmware-realtek libcom-err2 libext2fs2 libmagic-mgc libmagic1 libraspberrypi-bin libraspberrypi-dev libraspberrypi-doc libraspberrypi0 libss2 libssl1.1 libxml2 libxmuu1 openssh-client openssh-server openssh-sftp-server openssl raspberrypi-bootloader raspberrypi-kernel raspi-config rpi-eeprom rpi-eeprom-images ssh sudo wpasupplicant 32 upgraded, 7 newly installed, 0 to remove and 0 not upgraded. Need to get 133 MB of archives. After this operation, 3192 kB of additional disk space will be used. Do you want to continue? [Y/n]
Pretty neat! Although you're not actually running Raspbian, you can run Raspbian executables with the Raspbian root filesystem mounted as though you were actually running on your Raspberry Pi.
When you're done with the chroot, just exit that shell (Ctrl-D
If you want to undo everything else afterward:
$ sudo rm /mnt/pi_image/usr/bin/qemu-arm-static $ sudo umount /mnt/pi_image/boot $ sudo umount /mnt/pi_image $ sudo kpartx -dv /dev/loop0 $ sudo losetup -d /dev/loop0 $ sudo rmdir /mnt/pi_image
Keep in mind you're not really running Raspbian. You never booted the Raspbian kernel, and you can't test things that depend on Raspbian's init system, like whether networking works, let alone running the Raspbian X desktop or accessing GPIO pins. This is an ARM emulator, not a Raspberry Pi emulator.
If you want to read more about qemu user mode and how it lets you run binaries from other architectures, I recommend these links:
- Transparently running binaries from any architecture in Linux with QEMU and binfmt_misc
- Running ARM programs under linux (without starting QEMU VM!)
[ 16:14 Nov 03, 2019 More linux | permalink to this entry | comments ]