KISS

Keep It Simple Stupid

How I migrated Arch Linux ARM to another Pi architecture

| comments

For a small project of mine, I needed a small computer — I picked a Raspberry Pi 3B+ and setup the Arch Linux ARM there, which worked great. However I only used its Wi-Fi and GPIO ports, so that high-end model was an overkill. I decided to migrate to a smaller and cheaper one — Raspberry Pi Zero W. The 3B+ model uses an armv7 chip (to be precise, that’s an aarm64 (aka armv8), but I couldn’t make the GPIO ports work there a few months ago), and the zero W has an armv6 chip, so cloning the SD card directly doesn’t work (the binaries will be incompatible). My solution is to install the proper Arch Linux ARM for armv6 on the new SD card and then manually transfer the necessary configs/files from the old one. My way of doing that is below.

Accessing the original micro-SD card

My primary OS is OSX, which cannot mount ext4 partitions, so in order to access the original micro-SD card, I have a VirtualBox VM with Manjaro Linux (an Arch-based distro). A few tweaks to the machine’s settings were necessary:

  • in the Network tab, I disabled the first NIC with NAT and enabled the second one with the Bridged network — now the VM and the new rpi can see each other on the same Wi-Fi network;
  • forward the SD card to the VM with the excellent how-to Mount SD card in VirtualBox from Mac OS X Host:
    • find out the /dev/diskX device for your SD card from diskutil list, let’s say it’s /dev/disk2 for the examples below;
    • unmount it: diskutil unmountDisk /dev/disk2;
    • create a disk image mapped to the SD card: sudo VBoxManage internalcommands createrawvmdk -filename ~/vbox/sd-card-disk2.vmdk -rawdisk /dev/disk2;
    • unmount the disk again: diskutil unmountDisk /dev/disk2 (OSX mounts the disk very persistently and annoyingly after slightest changes);
    • change the permissions: sudo chmod 777 /dev/disk2 ~/vbox/sd-card/disk2.vmdk (there must be a better and more secure way);
    • finally, add the disk to the VM;
    • NB: if you previously created the device file for a smaller SD card, and then are using it with a bigger one, the VM will boot fine, but you’re very likely to see errors while mounting the partitions (something like “can’t read superblock”).

In order not to fill all the available space on the new SD card, I transferred the root directories from the old card one by one using netcat. I SSH’d into both computers and was able to run commands conveniently on both of them from my OSX in tmux’s split windows. In the VM, the preparations are:

1
2
3
4
5
6
7
# mount the main partition of the SD card
sudo mount -o ro /dev/sdb2 /mnt/root

# install `netcat`
sudo pacman -S gnu-netcat

cd /

Sending directories

For each directory in / (i.e., /etc, /usr, /home, etc.):

1
sudo tar c etc | netcat -l -p 9000

— this opens a listening TCP socket on port 9000 and when a client connects, it will tar the /etc directory and send to the client. As an alternative, if some files are too big and you don’t want to transfer them, you may want to tweak what is archived first:

1
2
sudo tar --exclude=usr/share/{vim,licenses,xml,i18n,locale,man,doc,kbd,fonts,zsh,info,X11} -cvf ~/usr.tar usr
netcat -l -p 9000 < ~/usr.tar

Receiving directories

On the new pi, go to the directory for the backups:

1
2
mkdir ~/bkp
cd ~/bkp

And now, once the VM’s socket is listening, we can fetch the archive:

1
netcat manjaro.local. 9000 | sudo tar xv

I’m using the VM’s multicast DNS name instead of an IP address; and sudo before tar is necessary to properly restore the file permissions. Note: the network connection stays open even when everything is transferred, so you need to Ctrl-C it when it’s done, the other side will then exit automatically.

Comparing the directories

Finally, we can compare the directories. I found the most convenient way to do that in the Compare Directories using Diff in Linux article:

1
sudo diff --no-dereference -ur {,/}etc > diff; vim diff

sudo is necessary to compare the system files; {,/}etc expands to etc /etc; and we open the result in vim. Now we have the diff file between the /etc on the old and new pies. Everything is smashed together, yet it’s very convenient to look at all those changes in one place. A piece of the file:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
Only in etc/conf.d: distccd
Only in etc/default: distcc
Only in etc: distcc
Binary files etc/ld.so.cache and /etc/ld.so.cache differ
diff --no-dereference -ur etc/makepkg.conf /etc/makepkg.conf
--- etc/makepkg.conf    2019-04-03 12:37:26.000000000 -0700
+++ /etc/makepkg.conf   2019-02-28 19:43:20.000000000 -0800
@@ -32,18 +32,18 @@
 # ARCHITECTURE, COMPILE FLAGS
 #########################################################################
 #
-CARCH="armv7h"
-CHOST="armv7l-unknown-linux-gnueabihf"
+CARCH="armv6h"
+CHOST="armv6l-unknown-linux-gnueabihf"

 #-- Compiler and Linker Flags
 # -march (or -mcpu) builds exclusively for an architecture
 # -mtune optimizes for an architecture, but builds for whole processor family
 CPPFLAGS="-D_FORTIFY_SOURCE=2"
-CFLAGS="-march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -O2 -pipe -fstack-protector-strong -fno-plt"
-CXXFLAGS="-march=armv7-a -mfloat-abi=hard -mfpu=vfpv3-d16 -O2 -pipe -fstack-protector-strong -fno-plt"
+CFLAGS="-march=armv6 -mfloat-abi=hard -mfpu=vfp -O2 -pipe -fstack-protector-strong -fno-plt"
+CXXFLAGS="-march=armv6 -mfloat-abi=hard -mfpu=vfp -O2 -pipe -fstack-protector-strong -fno-plt"
 LDFLAGS="-Wl,-O1,--sort-common,--as-needed,-z,relro,-z,now"
 #-- Make Flags: change this for DistCC/SMP systems
 #MAKEFLAGS="-j2"
diff --no-dereference -ur etc/pacman.conf /etc/pacman.conf
--- etc/pacman.conf     2019-03-05 07:56:33.000000000 -0800
+++ /etc/pacman.conf    2019-04-17 07:50:09.810254290 -0700
@@ -20,7 +20,7 @@
 #XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
 #CleanMethod = KeepInstalled
 #UseDelta    = 0.7
-Architecture = armv7h
+Architecture = armv6h

 # Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
 #IgnorePkg   =
@@ -31,7 +31,7 @@

 # Misc options
 #UseSyslog
-Color
+#Color
 #TotalDownload
 CheckSpace
 #VerbosePkgLists
Only in /etc: resolv.conf.bak

What it says is:

  • Only in etc/…: the distcc-related files exist only on the old pi, probably distcc isn’t installed here yet;
  • ld.so.cache files are binary and they differ, in most cases the already existing one is correct (because they can be architecture-dependent);
  • makepkg.conf — a few settings are different, and in this case we should leave them as is;
  • pacman.conf — we see two settings are different, Architecture should be left as it, as to Color: it was enabled before, but disabled now (by default), so if I want the change I need to apply the change manually by editing the /etc/pacman.conf file, for example by running sudo vimdiff {,/}etc/pacman.conf;
  • Only in /etc/…: the backup file exists only in the new setup, nothing to do here.

Using this regexp in vim: \v^(Only in|-{3}|\+{3}|Binary files), I can jump to the next/previous change very quickly. I also did :set nowrapscan so that I don’t jump back to the top when I’m finished with the file. Overall, it’s a manual process, but reasonably efficient as for me if you need to do it once or twice.

Based on the diff, you may need to update config files, install packages, remove packages, copy whole files (e.g. dotfiles from your old $HOME), remove files, start systemd services. Repeat for every directory in /.

That’s it. Don’t forget to make backups of the new OS.

Comments