[HOWTO] for newbies — installing headless Manjaro on Zero's eMMC

This is my first HOWTO in many years; so, please be kind. All feedback is welcome.

Using a Linux box:-

  1. Specify your WiFi ID, password, and image URL. See the Releases page for the latest URL; then, edit imgurl accordingly.
$ mywifiID=etaorionis
$ mywifiPW=whatever.the.pwd.is
$ mtpt=/tmp/mymountpoint
$ mydir=~/myradxa
$ imgurl=https://github.com/manjaro-arm/radxa-zero-images/releases/download/20220404/Manjaro-ARM-minimal-radxa-zero-20220404.img.xz
$ mkdir -p $mtpt $mydir/old
  1. Download important files. for the latest version. You’ll want the one whose name begins with Manjaro-ARM-minimal. The example assumes the date is 04/04/2022.
$ cd $mydir
$ ls *xz && mv -vf *xz old/
$ wget https://dl.radxa.com/zero/images/loader/rz-fastboot-loader.bin
$ wget https://dl.radxa.com/zero/images/loader/rz-udisk-loader.bin
$ wget https://dl.radxa.com/zero/images/loader/u-boot.bin.sd.bin
$ wget https://github.com/manjaro-arm/radxa-zero-images/releases/download/20220404/Manjaro-ARM-minimal-radxa-zero-20220404.img.xz -O my.img.xz
$ pip3 install pyamlboot
$ apt -y install fastboot
  1. Hold the USB Boot button down and connect Zero to Linux PC. Release the USB Boot button. lsusb should show the following VID/PID. If it doesn’t, retrace your steps and try again.
$ lsusb | grep -i amlogic
(...)Bus 002 Device 030: ID 1b8e:c003 Amlogic, Inc. GX-CHIP
$ boot-g12.py rz-fastboot-loader.bin
$ fastboot flashing unlock_critical
$ fastboot flashing unlock
$ fastboot erase bootloader
$ fastboot erase bootloader-boot0
$ fastboot erase bootloader-boot1
  1. Unplug Zero from your PC. Wait 10s. Hold down USB button and plug in again. Wait 10s. Release button. Type:-
$ boot-g12.py radxa-zero-erase-emmc.bin
  1. Unplug, wait 10s, hold USB button, plug in again, wait 10s, release button, and type:-
$ boot-g12.py rz-udisk-loader.bin
$ sync;sync;sync; sleep 10; sync;sync;sync
$ partition=$(ls /dev/sd* | sort | tail -n1)
$ disk=$(echo "$partition" | sed s/2// | sed s/1//)
$ file $disk || echo "Retrace your steps and try again."
  1. Prep the image.
$ pv -B 1M my.img.xz | xzcat | dd of=$disk bs=1024k
$ sync;sync;sync
$ finalprtno=$(fdisk -l $disk | grep -x "/dev/sd.*" | grep -n "" | tail -n1 | cut -d':' -f1)
$ seca=$(fdisk -l $disk | grep /dev/sd | tail -n1 | tr ' ' '\n' | grep -vx "" | grep -v /dev/sd | head -n1)
$ mkdir -p $mtpt
$ echo -en "d\n$finalprtno\nn\np\n$finalprtno\n$seca\n\nw\np\nn\nw\np\nw\n" | fdisk $disk
$ e2fsck -f $partition
$ resize2fs $partition
$ mount $partition $mtpt
$ [ -e "$disk"2 ] && mount "$disk"1 $mtpt/boot
$ mount devtmpfs $mtpt/dev -t devtmpfs
$ mount tmpfs $mtpt/tmp -t tmpfs
$ mount proc $mtpt/proc -t proc
$ mount sysfs $mtpt/sys -t sysfs
  1. Copy across your ssh key and resolv.conf files. (Run ‘ssh-keygen’ if you don’t have an ssh key.)
$ mkdir -p $mtpt/root/.ssh
$ cat /root/.ssh/*pub >> $mtpt/root/.ssh/authorized_keys
$ cat /etc/resolv.conf >> $mtpt/etc/resolv.conf
  1. Set up rc.local ersatz.
$ cat << EOF > $mtpt/etc/systemd/system/rc-local.service
[Unit]
Description=/etc/rc.local Compatibility
ConditionFileIsExecutable=/etc/rc.local

[Service]
Type=simple
ExecStart=/etc/rc.local

[Install]
WantedBy=multi-user.target
EOF

$ cat << 'EOF' > $mtpt/etc/rc.local
#!/bin/bash

mydev=$(nmcli dev | grep -x "w.*" | tr -s ' ' '\t' | cut -f1)
for j in 0 1 2 ; do
    if ! nmcli con | grep "$mydev" ; then
        sleep 30
        nmcli device wifi connect "YOURWIFI" password "YOURPASSWORD"
    fi
done
dmesg > /dmesg.txt
ifconfig > /ifconfig.txt
nmcli con > /nmcli_con.txt
nmcli dev > /nmcli_dev.txt
exit 0
EOF

$ sed -i s/YOURPASSWORD/$mywifiPW/ $mtpt/etc/rc.local
$ sed -i s/YOURWIFI/$mywifiID/ $mtpt/etc/rc.local
$ chmod +x $mtpt/etc/rc.local
$ chroot $mtpt systemctl enable rc-local.service
  1. Chroot into the new filesystem. Set locale, add packages, etc. Change root password. Create a non-root user ID.
$ chroot $mtpt
$ pacman-key --init
$ pacman-key --populate archlinux manjaro archlinuxarm
$ pacman-mirrors -c United_States                  # pacman-mirrors -call
$ pacman -Sy zstd
$ pacman -Syu
$ bash /usr/share/manjaro-arm-oem-install/manjaro-arm-oem-install
$ sed -i s/.*install// /root/.bash_profile
  1. Set up networking.
$ echo -en "2\ny\n" | pacman -S networkmanager iwd dhclient netctl net-tools
$ systemctl enable fstrim.timer sshd systemd-timesyncd NetworkManager
  1. Create ssh key for the Zero itself.
$ echo "PermitRootLogin yes" >> /etc/ssh/sshd_config
$ cd /root
$ ssh-keygen
  1. Quit the chroot. Unmount the chroot’s disk(s).
$ exit
$ kill $(lsof | grep $mtpt | tr -s ' ' '\t' | cut -f2 | sort | uniq)
$ sync;sync;sync;sleep 10
$ umount $mtpt/{dev,sys,tmp,proc} $mtpt/boot $mtpt
$ sync;sync;sync;sleep 10
  1. Unplug the Zero. Wait 10 seconds. Unplug all USB devices from the Zero, including the hub you’re using (if you’re using one). Plug the power in again. Wait 60 seconds.

  2. On the Linux box, try to locate the Zero:-

$ arp-scan --localnet
  1. If you can’t find the Zero’s IP address, please connect an HDMI monitor and an Ethernet-capable USB hub to the Zero; next, connect a keyboard and an Ethernet cable to the USB hub; finally, run ‘arp-scan --localnet’ a second time. This will come in handy if you’re troubleshooting.

  2. With the IP address that you’ve found, use ssh to log into the Zero.

3 Likes

You don’t need step 3 when you have step 4, which will also mounts the eMMC so no boot-g12.py rz-udisk-loader.bin is needed.

Nice.

I was going to say that you could turn this into a shell script and put it on github, but having the steps explained is good for security and for learning.

You might want to add other prerequisites on the linux host, eg. pv and binfmt-qemu…

In step 3, you can use something like watch lsusb or dmesg -w to get something close to a real-time view of usb devices.

Is there a reason for the 10s sleeps? I didn’t need to do that on my Zeros.

sync;sync;sync isn’t a good pattern - it’s kind of useless. If the kernel is still writing data to the device, another sync isn’t going to make it go any faster. A more effective strategy is something like for i in $(seq 6) ; do sync ; sleep 1; done which still runs sync, but at 1 second intervals hopefully giving time for the writes to complete. Better yet, you could use something like iostat to check if the kernel is still writing before proceeding to the next step, or to use one of the sync flags to dd (sync, fsync, fdatasync, dsync, direct, nocache)

Since you’re enabling root ssh logins, and creating an ssh host key, you might want to copy in the your ssh public key to /root/.ssh/authorized_keys so that your Zero is already provisioned for key auth.