Steps to first boot

This article explains the absolutely minimum steps that are required to get the development system to boot.

Boot requirements

To boot the system, you will need:

Tools requirements

All of the above are source packages; we need to build them. The tools required to build them are:

We will also need Fatdog's devx package (obviously) and 32-bit SFS package as the Linaro compiler above can only run on 32-bit operating system.

We use ready-made compilers instead of building our own to reduce the amount of fuss and potential problems.

Early success is important, because it provides confirmation that we are on the right path, and encouragement to explore further.

Using ready-made compilers known to work ensure that we focus on the system development process instead of tool development process. In later phases we will show how the tools get prepared.

Preparing the tools

1. Extract the two compilers anywhere, for the sake of discussion let's say that you extract linaro's one in /path/to/linaro (so that /path/to/linaro/bin will contain the binaries), and aboriginal's one in /path/to/abo (again, with the binaries in /path/to/abo/bin).

2. Add both compilers to your path:

export PATH=/path/to/linaro/bin:/path/to/abo/bin:$PATH

3. Load Fatdog's devx and the 32-bit SFS (Linaro's compiler requires it).

4. Build sunxi-tools - just run "make" in sunxi-tools directory.

Preparation done, we're good to do the actual build.

Building the boot-loader

Next, we build the boot loader (u-boot). We have to use Linaro's compiler (gcc 4.7) because aboriginal's one (gcc 4.2.1) will not compile it; is too old.

cd /path/to/u-boot-sunxi
make mele_a1000 CROSS_COMPILE=arm-linux-gnueabihf-
cp tools/mkimage to /usr/bin

Building the kernel

Next, we build the boot loader. As with u-boot, we need to use Linaro's tool here for the reason (aboriginal's compiler cannot build armv7 code).

cd /path/to/linux-sunxi
git checkout origin/sunxi-3.0
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- sun4i_defconfig
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- -j5 uImage modules
make ARCH=arm CROSS_COMPILE=arm-linux-gnueabihf- modules_install INSTALL_MOD_PATH=output

While the kernel is compiling, you may want to start building script.bin. This is a hardware configuration file that the kernel will later use to initialise and configure the devices on the A10 SoC. In newer kernels, this file is replaced with "device tree", but since we're using the default 3.0 kernel, we still need this.

There isn't any equivalent of script.bin or device tree in x86/x86_64 desktop world. On desktop world, major devices are partly initialised by the system's firmware (BIOS or UEFI). Any other devices which aren't are discoverable using PCI BIOS services and the kernel can use these services to get the details of the devices so that it can initialise them (provided that the kernel comes with the right drivers).

There is no BIOS in the ARM (or any other embedded) system, thus all the hard work must be done by the boot loader and the kernel - mostly the kernel.

The use of script.bin and/or device-tree is considered as a major advancement in Linux kernel development as it separates the board-specific settings from architecture-specific settings (e.g the same A10 SoC may be made into two different boards - one for tablets, one for settop boxes), and it enables one generic kernel to be used on different boards, by providing a separate configuration file for each.

The configuration settings for many A10-based boards are kept in sunxi-boards. They are stored in human readable form (called *fex* format), which must be converted into a binary format usable by the format. That's what the tools in sunxi-tools are for.

For my development system:

cd /path/to/sunxi-boards
cd sys_config
cd a10
cp mele_a1000.fex myconfig.fex
geany myconfig.fex (see below)
/path/to/sunxi-tools/fex2bin myconfig.fex script.bin

Before you convert the file, there is one information which needs to be modified - the MAC address. This specified here would become the MAC address of the ethernet (and on some systems, the wireless LAN too). Edit the file, find the following sections, and change the MAC address to your original system's MAC address.

Change the zeros with your own MAC address (invent one if you don't know).

MAC = "000000000000"

Building busybox

You only need the shell applet to get a minimal boot. But that would get boring pretty quickly, as you can't do much with it. I suggest you enable a more applets.

The only caveat here is that busybox must be configured as static binary. Then build it like this:

cd /path/to/busybox-source
make menuconfig
make CROSS_COMPILE=armv6l-

We use aboriginal compiler so that our static binary is smaller.

Preparing SD Card

There are only three simple rules in preparing the SD Card. First, you need at least 2 partitions. Second, the first one must be FAT. Third, it has to start at last on 1MB boundary and at least 32MB big.

You can make the second partition anything that supports being a root filesystem for Linux, and its size can be anything depending on what you plan to put on it.

For me, I created 256 MB FAT partition and 32 MB ext4 partition (for I will need the space on the FAT partition much later; and the second partition can easily be extended if needed).

Installing boot loader

Once the SD card has been partitioned, install the boot loader. Note: Replace /dev/sdXXX with the device that represents your SD Card. If you specify the wrong device you can easily and permanently wipe off the data from your disk, so be extra careful. You have been warned.

/dev/sdXXXX1 represents the first partition of /dev/sdXXX, we will mount it to /mnt/sdcard.

cd /path/to/u-boot-sunxi
dd if=spl/sunxi-spl.bin of=/dev/sdXXX bs=1024 seek=8
dd if=u-boot.bin of=/dev/sdXXX bs=1024 seek=32
mount /dev/sdXXX1 /mnt/sdcard
cp ./sunxi-boards/sys_config/a10/script.bin /mnt/sdcard

Configuring the boot loader

To configure the boot loader (tell it what kernel to load, what kernel parameters to pass), you create a boot.scr file (this is similar to menu.lst or syslinux.conf, although much simpler - simply because there isn't a simple way to access its full capabilities without a serial terminal).

Unlike syslinux.conf and menu.lst which are simple text files, boot.scr is a binary file which needs to be "compiled" from it source text file. The text file is commonly called boot.cmd although of course you can name it anything.

The u-boot-sunxi comes with default configuration (which can be changed by recompiling it - it's inside u-boot-sunxi/include/configs/sunxi-common.h) and will boot without boot.scr, but it is so much better to change the boot.scr rather than rebuilding u-boot each time.

Create a file calld boot.cmd somewhere. To make it easier, do it on /mnt/sdcard itself (so you'll remember the boot.cmd that corresponds to your boot.scr. Put the following entries in it:

fatload mmc 0 0x43000000 script.bin
fatload mmc 0 0x48000000 uImage
bootm 0x48000000

Then save the file, and convert it to boot.scr this way:

mkimage -C none -A arm -T script -d boot.cmd boot.scr

Copy boot.scr to /mnt/sdcard if you didn't create it there.

When done, create another file called uEnv.txt on /mnt/sdcard. This file contains "variables" which can be passed to boot.scr. Again, this file isn't strictly needed but since it is a regular text file (no conversion is necessary), it is handy to use to specify variables that we may want to change later. For now, we will only keep the variable "bootargs" which represents the command line to be passed to the kernel.


bootargs=console=tty0 root=/dev/mmcblk0p2 rootwait panic=10

which means, use tty0 as the console (which will be the framebuffer console, that is, your TV screen) as opposed to ttyS0 which is the serial console (which you can't see unless you attach an USB-to-serial to your board); user /dev/mmcblk0p2 as the root device (second partiton of your SD card --- we don't use /dev/sdXXX2 because /dev/sdXXX2 is how we see it from our system; when the kernel boots up this is how it will see the partition); rootwait means wait as long as needed until the root device appears (SD card can be slow to initialise), and the last one tells the kernel to wait 10 seconds before automatically rebooting if it "panics".

If you don't want to use uEnv.txt you could have done the same by adding a line
setenv bootargs console=tty0 root=/dev/mmcblk0p2 rootwait panic=10
anywhere before bootm in boot.cmd.

Installing kernel

With the /dev/sdXXX1 still mounted on /mnt/sdcard, do:

cd /path/to/linux-sunxi
cp arch/arm/boot/uImage

Preparing root filesystem

Umount /dev/sdXXX1, and mount /dev/sdXXX2 (this would be the second partition of the SD card). Let's assume it is mounted in /mnt/sdcard.

You need to create at least /dev, /bin and /sbin. Put busybox binaries in /bin and create the necessary applet links (at least shell is needed), and create a shell script in /sbin/init, like this:

umount /mnt/sdcard
mount /dev/sdXXX2 /mnt/sdcard
cd /mnt/sdcard
mkdir dev bin sbin
cp /path/to/busybox-source/busybox bin/busybox
ln -s busybox bin/sh

touch sbin/init
cat > sbin/init << EOF
exec /bin/sh

If you wish to experiment with modules, copy all the modules from the kernel too (of course when you would need busybox to have module-utils too, otherwise there is no way to use them).

cp -a /path/to/linux-sunxi/output/lib .

That's all!

Boot process

Every SoC boot differently. The following is specific to A10 (and only in SD Card boot mode), so take this as an illustration rather than as a hard fact (I'm also simplifying things):

  1. A10 starts up and execute code in BROM (Boot ROM) - outside our control
  2. BROM loads spl.bin from block 8kb of SD Card (to address 0x20 in SRAM) and jumps to it
  3. spl.bin loads u-boot from block 32kb of SD Card (to address 0x4A000000 in DRAM) and jumps to it
  4. u-boot loads:
    1. Runs its internal script, which loads:
      1. uEnv.txt (setting values to variables)
      2. boot.scr, which will:
        1. Load kernel (uImage) at address 0x48000000
        2. Load script.bin (the hardware configuration) at address 0x43000000
        3. Parse and run any other commands
        4. Starts the kernel (bootm command)
          1. bootm relocates kernel from 0x48000000 to 0x40008000 (ZRELADDR) and starts executing virtual address 0xC0008000 (PAGE_OFFSET of 0xC0000000 and TEXT_BASE of 0x8000)
  5. Kernel (uImage) starts
    1. Read hardware config from 0x43000000 and initialise A10 SoC
    2. Kernel mounts the root filesystem (/dev/mmcblk0p2)
    3. Kernel launches /sbin/init
    4. /sbin/init launches our shell.
  6. Shell prompt

Our next step would be to compile AUFS-enabled kernel and see where and how we can load initrd.