Debian Cross-compile

This work in progress procedure will create an image that can be copied onto an SD card and be used to boot a Mirabox. It aims to use a stock kernel and stock Debian.

Pre-requisites

I am assuming you are running Debian 9 (Stretch) on 32-bit or 64-bit Intel PC or Server. I have tested this procedure on a minimal install on a VM.

You will require root access using sudo.

Step 1 - Decide where to deploy everything

You will need around 4GB of free disk space for the git tree and the chroot environment. For this demonstration I will use the MIRABUILD environment variable as the root of the tree.

MIRABUILD=~/mirabuild
export MIRABUILD

The very first time you start, you'll need to create the build directory:

mkdir $MIRABUILD

Don't forget to re-export the environment variable if you repeat parts of the procedure.

Step 2 - install the required packages

Debian now includes a GCC cross-compiler for ARM as well as a really easy mechanism to create a chroot environment for a foreign architecture using Qemu. We'll use both of those in this procedure.

Use apt-get to install the required packages.

sudo apt-get install --no-install-recommends gcc-arm-linux-gnueabihf build-essential git ca-certificates bc binfmt-support qemu-user-static debootstrap parted dosfstools rsync

Step 3 - Clone the Linux-stable git repository

We will use the linux-stable git repository and install using the latest stable version at the time of writing.

cd $MIRABUILD
git clone git://git.kernel.org/pub/scm/linux/kernel/git/stable/linux-stable.git

If you have already cloned the repository and want to bring it up to date with the latest stable version, perform the following:

cd $MIRABUILD/linux-stable
git checkout -- .
git checkout master
git pull

Step 4 - Checkout the desired stable version

We will checkout the latest stable version at the time of writing.

cd $MIRABUILD/linux-stable
git checkout v4.14.16
git checkout -b v4.14.16-mirabox

Step 4 - Build the Linux kernel

I will use my personal Linux kernel configuration. You can use this as a base and amend to your liking. Be careful! This configuration probably will not work with systemd as it does not include several required features (e.g. cgroups). Also, make sure the resultant kernel is less than 2MB in size if you intend to flash it to NAND.

I also have a small patch to disable detection of the Marvell SD8787 Bluetooth AMP device, which doesn't work with the SD8787 firmware I use.

cd $MIRABUILD/linux-stable
wget -O.config https://www.solinno.co.uk/public/mirabox/config-mirabox-4.14.16
wget -O- https://www.solinno.co.uk/public/mirabox/mirabox-sdio.patch | patch -p1
export CROSS_COMPILE=arm-linux-gnueabihf-
make ARCH=arm -j3

Step 5 - Create the ARM chroot environment

We'll use an ARM chroot environment both to help create the kernel image and initial ramdisk, but also to let us create an SD card image. Change the Debian mirror to be one appropriate to your country.

Note that I remove dmsetup as without this the initial ramdisk becomes too large. If you need dmsetup then you will need to disable the initramfs hook after it is installed.

cd $MIRABUILD
sudo qemu-debootstrap --arch=armhf --include=sysvinit-core,locales,ntp,vim-tiny,initramfs-tools,u-boot-tools,xz-utils stretch $MIRABUILD/debian_armhf_stretch http://ftp.debian.org/debian/
sudo chroot $MIRABUILD/debian_armhf_stretch apt-get remove -y --purge dmsetup systemd
sudo chroot $MIRABUILD/debian_armhf_stretch apt-get clean
sudo mkdir $MIRABUILD/debian_armhf_stretch/uboot

Step 6 - Trim the size of the chroot environment

To keep the chroot environment as small as possible I install a dpkg.cfg file that excludes man pages, documentation and non-English locales. You may edit this to your needs, if required.

cd $MIRABUILD/debian_armhf_stretch
wget -O- https://www.solinno.co.uk/public/mirabox/etc_dpkg_dpkg.cfg | sudo tee etc/dpkg/dpkg.cfg >/dev/null
sudo rm -fr $MIRABUILD/debian_armhf_stretch/usr/share/man/*
sudo rm -fr $MIRABUILD/debian_armhf_stretch/usr/share/doc/*
ls -d $MIRABUILD/debian_armhf_stretch/usr/share/locale/*/LC_MESSAGES | sed -e 's/\/LC_MESSAGES$//' -e 's/^/rm -fr /' | grep -v -e 'locale/en' -e '/uk$' | sudo sh

Step 7 - Configure the chroot initramfs

In order to keep the initial ramdisk as small as possible we configure it to use xz compression and just include the modules needed to get the root filesystem mounted. NB: This configuration is suitable for booting from the SD Card using the ext4 filesystem.

cd $MIRABUILD/debian_armhf_stretch 
wget -O- https://www.solinno.co.uk/public/mirabox/etc_initramfs-tools_initramfs.conf | sudo tee etc/initramfs-tools/initramfs.conf >/dev/null
wget -O- https://www.solinno.co.uk/public/mirabox/etc_initramfs-tools_modules | sudo tee etc/initramfs-tools/modules >/dev/null

NOTE: The fsck attempted by the initial ramdisk will fail when the mirabox boots, however this won't prevent booting. You can correct the problem by recreating the initial ramdisk on the mirabox itself (where it will be able to correctly determine the root filesystem).

Step 8 - Deploy the kernel into the chroot environment

This step copies the kernel and modules into the chroot environment and creates the u-boot images that can be loaded by the Mirabox.

Firstly, we will create the kernel image. Because the verison of u-boot shipped with the Mirabox is too old to pass a DTB to the bootloader, we will have to append the DTB to the kernel. We set the option to enable this in the kernel configuration downloaded in step 4.

cd $MIRABUILD/linux-stable
RELEASE=$(make ARCH=arm kernelrelease)
sudo cp arch/arm/boot/zImage $MIRABUILD/debian_armhf_stretch/boot/vmlinuz-$RELEASE
cat arch/arm/boot/dts/armada-370-mirabox.dtb | sudo tee -a $MIRABUILD/debian_armhf_stretch/boot/vmlinuz-$RELEASE >/dev/null
sudo chroot $MIRABUILD/debian_armhf_stretch mkimage -A arm -O linux -C none -a 0x00008000 -e 0x00008000 -n uImage-$RELEASE -d /boot/vmlinuz-$RELEASE /uboot/uImage-$RELEASE

Secondly, we will install the kernel modules.

cd $MIRABUILD/linux-stable
make ARCH=arm modules
sudo make ARCH=arm INSTALL_MOD_PATH=$MIRABUILD/debian_armhf_stretch modules_install

Thirdly, we will create the initial ramdisk.

sudo chroot $MIRABUILD/debian_armhf_stretch update-initramfs -c -k $RELEASE

If you've already created an initial ramdisk and need to update, use this instead:

sudo chroot $MIRABUILD/debian_armhf_stretch update-initramfs -u -k $RELEASE

Now, create the u-boot initial ramdisk image:

sudo chroot $MIRABUILD/debian_armhf_stretch mkimage -A arm -O linux -T ramdisk -C none -a 0 -e 0 -n uInitrd-$RELEASE -d /boot/initrd.img-$RELEASE /uboot/uInitrd-$RELEASE

Step 9 - Install some additional packages required for the Mirabox

We will now install some additional packages that are appropriate for a Mirabox. These include the utilities for PCI, USB, MTD and Wireless components. Also, I use the busybox-syslogd as it doesn't continually write to the filesystem, thus preserving the life of the NAND flash.

sudo chroot $MIRABUILD/debian_armhf_stretch apt-get install --purge -y bluez crda iw mtd-utils pciutils usbutils wireless-regdb wpasupplicant busybox-syslogd

While we are on, remove a package or two we don't need. If you think you need any of these packages, don't remove them!

sudo chroot $MIRABUILD/debian_armhf_stretch apt-get remove --purge dmidecode tasksel tasksel-data debconf-i18n gnupg gnupg-agent

Finally, we will install the SD8787 firmware so that the onboard wireless works. Note! This is for v1 and v2 of the Mirabox. v3 uses a different wireless chip, and I don't have access to one.

sudo mkdir -p $MIRABUILD/debian_armhf_stretch/lib/firmware
wget -O- 'https://git.kernel.org/pub/scm/linux/kernel/git/firmware/linux-firmware.git/plain/mrvl/sd8787_uapsta.bin' | sudo tee $MIRABUILD/debian_armhf_stretch/lib/firmware/sd8787_uapsta.bin >/dev/null

Step 10 - Configure the chroot environment for the Mirabox

Before we create the SD Card image, we need to set up the chroot environment so that when the Mirabox boots from it, things will work correctly.

Set the hostname of the image.

echo mirabox | sudo tee "$MIRABUILD/debian_armhf_stretch/etc/hostname" >/dev/null

Create a suitable filesystem table (/etc/fstab).

wget -O- https://www.solinno.co.uk/public/mirabox/etc_fstab | sudo tee "$MIRABUILD/debian_armhf_stretch/etc/fstab" >/dev/null

Create a suitable inittab placing a getty on the Mirabox serial port.

wget -O- https://www.solinno.co.uk/public/mirabox/etc_inittab | sudo tee "$MIRABUILD/debian_armhf_stretch/etc/inittab" >/dev/null

Add the configuration file for fw_printenv and fw_setenv so the u-boot configuration and be modified from the operating system.

wget -O- https://www.solinno.co.uk/public/mirabox/etc_fw_env.config | sudo tee "$MIRABUILD/debian_armhf_stretch/etc/fw_env.config" >/dev/null

Set the root password so we can login the first time we boot.

echo 'root:mirabox' | sudo chpasswd -c SHA512 -R "$MIRABUILD/debian_armhf_stretch"

Step 11 - Create the SD Card image

We will now create an SD card image with a small MS-DOS partition and filling the rest of the image with an ext4 partition. In the dd command seek=1886 will create a (slightly less than) 2GB image suitable for a typical SD card. You may need to check your SD card to see how many sectors are on it to get this precisely right.

rm -f $MIRABUILD/sdcard.img
dd if=/dev/zero of=$MIRABUILD/sdcard.img bs=1024k count=0 seek=1886
sudo losetup /dev/loop0 $MIRABUILD/sdcard.img
sudo parted -s /dev/loop0 mklabel msdos
sudo parted -s /dev/loop0 unit s mkpart primary fat32 -- 2048 64MB
sudo parted -s /dev/loop0 set 1 boot on  
sudo parted -s /dev/loop0 unit s mkpart primary ext4 -- 64MB -1

Now, create the filesystems on the image:

sudo mkfs.vfat -n miraboot /dev/loop0p1  
sudo mkfs.ext4 -L miraroot /dev/loop0p2

Don't worry about the warning issued by mkfs.vfat, it won't affect the Mirabox.

Step 12 - Mount the SD Card image

Now we create loop devices for the indivual partitions, and mount them.

sudo fdisk -l /dev/loop0 | awk '/loop0p1/{print "losetup /dev/loop1 '$MIRABUILD'/sdcard.img -o " $3 * 512 " --sizelimit " $4 * 512}' | sudo sh
sudo fdisk -l /dev/loop0 | awk '/loop0p2/{print "losetup /dev/loop2 '$MIRABUILD'/sdcard.img -o " $2 * 512 " --sizelimit " $3 * 512}' | sudo sh
sudo mount -t ext4 /dev/loop2 $MIRABUILD/sysimage
sudo mkdir $MIRABUILD/sysimage/uboot
sudo mount -t vfat /dev/loop1 $MIRABUILD/sysimage/uboot

Step 13 - Populate the SD Card image

In this step, we will copy the files across from the chroot environment to the mounted filesystems on the SD Card image file.

sudo rsync -a $MIRABUILD/debian_armhf_stretch/ $MIRABUILD/sysimage/

We can check to see how full the filesystems are, there should be plenty of space free.

df -h $MIRABUILD/sysimage $MIRABUILD/sysimage/uboot

Once we are happy, unmount the filesystems and remove the loop devices.

sudo umount $MIRABUILD/sysimage/uboot
sudo umount $MIRABUILD/sysimage
sudo losetup -d /dev/loop2
sudo losetup -d /dev/loop1
sudo losetup -d /dev/loop0

Step 14 - Copy the SD Card image to a real SD card

This step is DANGEROUS! Make sure you select the correct device for your SD Card reader. Replace /dev/sdXXXXX with the correct device. Check three times before proceeding.

sudo dd if=$MIRABUILD/sdcard.img of=/dev/sdXXXXX bs=2048

Step 15 - Boot on the Mirabox

Interrupt autoboot then type in the following commands to the Marvell> prompt.

usb start
fatload usb 1 0x6400000 uImage-4.14.16+
fatload usb 1 0x7400000 uInitrd-4.14.16+
setenv bootargs console=ttyS0,115200 root=LABEL=miraroot rootdelay=4
bootm 0x6400000 0x7400000

All being well, you now have an SD card that you can manually boot a recent Debian version with a recent kernel.

When I get time I will add instructions to transfer the SD card image to the onboard flash.

If any part of this procedure does not work, please contact leigh AT solinno DOT co DOT uk for assistance.

Step 16 - Rough notes for the moment - ignore for now

To erase kernel and initial ramdisk area in one go:

nand erase 0x400000 0x400000

To erase kernel and initial ramdisk individually:

nand erase 0x400000 0x200000
nand erase 0x600000 0x200000

To write kernel and initial ramdisk:

nand write 0x6400000 0x400000 0x200000
nand write 0x7400000 0x600000 0x200000

To load kernel and initial ramdisk from nand and boot:

nand read 0x6400000 0x400000 0x400000
bootm 0x6400000 0x6600000

Changelog

V1.0 2018-02-04 Initial release.
V1.1 2018-02-05 Add download of SD8787 firmware. Fix patching kernel.
V1.2 2018-02-06 Add note about initial ramdisk fsck failure.
V1.3 2018-02-06 Add download of /etc/fw_env.config for fw_printenv.