I continued where I left off from my previous post with a USB Armory and an FT232RL connected thru UART for communication to host.

Compiling the kernel

Turns out it's just vanilla kernel with custom config. The only vendor specific things beside the kernel config are the kernel device trees and a driver for some security controller specific to i.MX53 SoC.

First, I downloaded the source packages, which the kernel source code from kernel.org as well as the NXP security controller driver.

export LINUX_VER=6.6.24 LOCALVERSION=-0

wget https://www.kernel.org/pub/linux/kernel/v6.x/linux-${LINUX_VER}.tar.xz
wget https://github.com/usbarmory/mxc-scc2/archive/master.zip -O mxc-scc2-master.zip

Then I downloaded the configs and DTSs from the USB armory repo

cat > dts_list.txt << EOF
imx53-usbarmory-host.dts
imx53-usbarmory-gpio.dts
imx53-usbarmory-spi.dts
imx53-usbarmory-i2c.dts
imx53-usbarmory-scc2.dts
EOF
wget -B "https://raw.githubusercontent.com/usbarmory/usbarmory/master/software/kernel_conf/mark-one/" -i dts_list.txt -C dts
wget https://raw.githubusercontent.com/usbarmory/usbarmory/master/software/kernel_conf/usbarmory_linux-6.6.defconfig

Time to start building

tar -xf linux-${LINUX_VER}.tar.xz
cd linux-${LINUX_VER}
make mrproper
cp ../usbarmory_linux-6.6.defconfig .config
LOCALVERSION=${LOCALVERSION} ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8 olddefconfig zImage modules nxp/imx/imx53-usbarmory.dtb

cp ../dts/* arch/arm/boot/dts/nxp/imx/
LOCALVERSION=${LOCALVERSION} ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- make -j8 nxp/imx/imx53-usbarmory-host.dtb nxp/imx/imx53-usbarmory-gpio.dtb nxp/imx/imx53-usbarmory-spi.dtb nxp/imx/imx53-usbarmory-i2c.dtb nxp/imx/imx53-usbarmory-scc2.dtb

cd ..

unzip -o mxc-scc2-master.zip
cd mxc-scc2-master
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- KERNEL_SRC=../linux-${LINUX_VER} -j8 all

Last but not least, all the final output files are put under a single directory.

mkdir -p kernel-final/{boot,lib/modules}

cd linux-${LINUX_VER}
make INSTALL_MOD_PATH=../kernel-final ARCH=arm modules_install
cp -r arch/arm/boot/zImage kernel-final/boot/zImage-${LINUX_VER}${LOCALVERSION}-usbarmory
cp -r .config kernel-final/boot/config-${LINUX_VER}${LOCALVERSION}-usbarmory
cp -r System.map kernel-final/boot/System.map-${LINUX_VER}${LOCALVERSION}-usbarmory
cp -r arch/arm/boot/dts/nxp/imx/imx53-usbarmory.dtb ../kernel-final/boot/imx53-usbarmory-default-${LINUX_VER}${LOCALVERSION}.dtb
cp -r arch/arm/boot/dts/nxp/imx/imx53-usbarmory-host.dtb ../kernel-final/boot/imx53-usbarmory-host-${LINUX_VER}${LOCALVERSION}.dtb
cp -r arch/arm/boot/dts/nxp/imx/imx53-usbarmory-spi.dtb ../kernel-final/boot/imx53-usbarmory-spi-${LINUX_VER}${LOCALVERSION}.dtb
cp -r arch/arm/boot/dts/nxp/imx/imx53-usbarmory-gpio.dtb ../kernel-final/boot/imx53-usbarmory-gpio-${LINUX_VER}${LOCALVERSION}.dtb
cp -r arch/arm/boot/dts/nxp/imx/imx53-usbarmory-i2c.dtb ../kernel-final/boot/imx53-usbarmory-i2c-${LINUX_VER}${LOCALVERSION}.dtb
cp -r arch/arm/boot/dts/nxp/imx/imx53-usbarmory-scc2.dtb ../kernel-final/boot/imx53-usbarmory-scc2-${LINUX_VER}${LOCALVERSION}.dtb

cd ../mxc-scc2-master
make INSTALL_MOD_PATH=../kernel-final ARCH=arm KERNEL_SRC=../linux-${LINUX_VER} modules_install
rm -f kernel-final/lib/modules/${LINUX_VER}${LOCALVERSION}/{build,source}

Following the convention of Alpine Linux, I moved all the imx53-usbarmory-*.dtb to a single directory under boot, which I promptly named dtbs-usbarmory. I then copied the content of kernel-final/boot into the boot directory of micro SD. The original Makefile would have me linking imx53-usbarmory.dtb to imx53-usbarmory-default-6.624-0.dtb, but FAT filesystems don't support symlink. So I simply copied it instead.

Modifying the modloop

A modloop is simply a squashfs image.

just modify the modloop to add the kernel modules

unsquashfs modloop-lts
rm squashfs-root/modules/*
cp kernel-final/modules/* squashfs-root/modules/ 
mksquashfs .modloop squashfs-root/ ~/Project/usbarmory/modloop-usbarmory -b 1048576 -comp xz -Xdict-size 100% -all-root

Turns out xz is not supported in the usbarmory kernel config. So I had to redo it with gzip compression, which is the default.

mksquashfs .modloop squashfs-root/ ~/Project/usbarmory/modloop-usbarmory -b 1048576 -all-root

Modifying the initramfs

Now I need to add the modules to the initramfs as well.

I thought about generating it like a civilized person. Just like any Linux distro you can think of, Alpine has a utility for generating initramfs, this one is called mkinitfs. I tried it, but it just didn't work out. So I resorted to repackaging an existing one from the tar package.

First, I extracted the initramfs.

mkdir /tmp/initramfs ; cd /tmp/initramfs
gunzip -c '/mnt/mmcblk0p1/boot/initramfs-lts' | cpio -i

Then I removed the modules of the old kernel and placed the new ones I built.

rm -r lib/modules/*
cp -r ~/Project/usbarmory/kernel/kernel-final/lib/modules/6.6.24-0/ lib/modules/

Finally, I repacked it.

find | cpio -o -H newc | gzip > /tmp/initramfs-6.6.24-0-usbarmory

After inspecting the result, I was satisfied, and copied the initramfs to the boot directory of micro SD.

Alpine Overlay

Usually, setting up Alpine requires a screen and a keyboard. But that's not possible with USB Armory since it has none of those.

While I've been doing just fine with the UART, I'd rather have SSH just like how it was with the stock OS.

Thankfully, somebody created this thing that sets up the network and enables SSH upon boot.

The original Alpine tar package comes with an overlay called alpine.ovlapk.tar.gz. However, this doesn't seem to play nicely when I try to have the headless.ovlapk.tar.gz. I suspect the system doesn't seem to like it when I have multiple overlay.

In the end, I got rid of alpine.ovlapk.tar.gz and just have headless.ovlapk.tar.gz on the root of my micro SD.

For static IP, I added a file called interfaces on the root of my micro SD.

auto usb0
allow-hotplug usb0
iface usb0 inet static
    netmask 255.255.255.0
    address 10.0.100.1
    gateway 10.0.100.2

extlinux

Last but not least, I modified extlinux/extlinux.conf in the micro SD.

Most importantly, I changed KERNEL and INITRD parameters to include my new kernel and initramfs. FTDDIR was also changed accordingly.

On the APPEND parameter, which is the kernel command line, I ensured to have ledtrig-heartbeat as well as g_ether in the modules option which contains a comma-separated list of modules to load upon boot. I also included the modloop option to point to the modloop that I made.

As a finishing touch, I changed all the labels from lts to usbarmory to better represent the current configuration. I also shortened the TIMEOUT from 10 to 3 seconds.

My extlinux/extlinux.conf then became:

TIMEOUT 3
PROMPT 1
DEFAULT usbarmory

LABEL usbarmory
MENU LABEL Linux usbarmory
KERNEL /boot/zImage-6.6.24-0-usbarmory
INITRD /boot/initramfs-6.6.24-0-usbarmory
FDTDIR /boot/dtbs-usbarmory
APPEND modules=loop,squashfs,sd-mod,usb-storage,ledtrig-heartbeat,g_ether quiet modloop=/boot/modloop-usbarmory

Making Connections

When I connected the USB Armory to my PC, it finally appeared as a network device!

The network setting on my PC needs to be set manual, with the IP of 10.0.100.2 and gateway of 10.0.100.1, which is opposite of what's on the interface file above.

I was able to then ssh in with ssh root@10.0.100.1 without any issue.

The USB Armory itself is not automatically connected to the internet. For this, I simply put a NAT forwarding on my PC thru iptables.

sudo iptables -t nat -A POSTROUTING -s 10.0.100.1/32 -o wlp3s0 -j MASQUERADE

And voila, I was able to ping the internet this way!

Next up: further customizations thru overlay!

Ref:

  • https://github.com/macmpi/alpine-linux-headless-bootstrap
  • https://github.com/usbarmory/usbarmory-debian-base_image