An all mainline Raspberry Pi 3

mainline is fun

Table of contents

Next: SD card setup, Previous: Top, [Index]

1 Introduction

In this page I am going to start from a bare SD Card to a bootable system by using only mainline uboot and kernel.

Next: SD card setup , Previous: Introduction, [Index]

2 SD card partitioning

We need two partitions, one for the booting firmwares and bootloader and one that will contain the filesystem in which we will boot userspace.

|                 |                |                  |
|    Partition    |   file system   |      size        |
|                 |                |                  |
|                 |                |                  |
|        1        |       vfat     |      64MB        |
|                 |                |                  |
|                 |                |                  |
|        2        |       ext4     |  the remaining   |
|                 |                | (8GB in my case) |
|                 |                |                  |

I will use fdisk and the device will be /dev/sda:

	# fdisk /dev/sda

Erase all the partitions

	Command (m for help): o
	Created a new DOS disklabel with disk identifier 0x40a592c7.

Create the first partition that will be primary 64MB

	Command (m for help): n
	Partition type
	   p   primary (0 primary, 0 extended, 4 free)
	   e   extended (container for logical partitions)
	Select (default p):  [ ENTER ] 
	Partition number (1-4, default 1):  [ ENTER ] 
	First sector (2048-15353855, default 2048):  [ ENTER ] 
	Last sector, +sectors or +size{K,M,G,T,P} (2048-15353855, default 15353855): +64M
	Created a new partition 1 of type 'Linux' and of size 64 MiB.

Set the partition as vfat:

	Command (m for help): t
	Selected partition 1
	Hex code (type L to list all codes): b
	Changed type of partition 'Linux' to 'W95 FAT32'.

Create the next primary partition, number 2, that will take the remaining space in the memory:

	Command (m for help): n
	Partition type
	   p   primary (1 primary, 0 extended, 3 free)
	   e   extended (container for logical partitions)
	Select (default p):  [ ENTER ] 
	Partition number (2-4, default 2):  [ ENTER ] 
	First sector (133120-15353855, default 133120):
	Last sector, +sectors or +size{K,M,G,T,P} (133120-15353855, default 15353855):  [ ENTER ] 

	Created a new partition 2 of type 'Linux' and of size 7.3 GiB.


by default the new partition will be of type Linux (ext2/4). The final result should look like this:

	Command (m for help): p
	Disk /dev/sda: 7.3 GiB, 7861174272 bytes, 15353856 sectors
	Units: sectors of 1 * 512 = 512 bytes
	Sector size (logical/physical): 512 bytes / 512 bytes
	I/O size (minimum/optimal): 512 bytes / 512 bytes
	Disklabel type: dos
	Disk identifier: 0x2d4a6c29
	Device     Boot  Start      End  Sectors  Size Id Type
	/dev/sda1         2048   133119   131072   64M  b W95 FAT32
	/dev/sda2       133120 15353855 15220736  7.3G 83 Linux

Finally, we write all the changes in the partition table:

	Command (m for help): w
	The partition table has been altered.
	Calling ioctl() to re-read partition table.
	Syncing disks.

Now that the partitioning is done, we need to create the filesystem, i.e. format the partitions:

	# mkfs.vfat /dev/sda1
	# mkfs.ext4 /dev/sda2

Next: u-boot, Previous: SD card setup, [Index]

3 Boot firmware

Once the SD card is set up, partitioned and formatted, it's time to prepare the boot partition by copying the necessary files that are required to boot the system on the Raspberry Pi:

	# mount /dev/sda1 /mnt/

The boot firmware is distributed in a git repository:

	$ git clone git:// rpi-firmware

From there we will need to copy the firmware that will perform the first initialization before starting u-boot:

	# cp rpi-firmware/boot/bootcode.bin /mnt/
	# cp rpi-firmware/boot/start.elf /mnt/
	# cp rpi-firmware/boot/fixup.dat /mnt/

We still need to setup config.txt, the runtime boot firmware configuration file. We can tell which kernel or bootloader to load after the firmware is executed:

	# vim /mnt/config.txt


Next: boot.scr, Previous: Boot firmware, [Index]

4 u-boot

u-boot is the higher level bootloader that we will use to boot the device and load the kernel with the kernel properties. But first we need to pull it from its repository:

	$ git clone git://
	$ cd u-boot

Now we will need to compile it, fist you need to make sure to have the cross compiler in your system. Setting it up is out of my scope, but don't forget to export it in the system:

	$ export CROSS_COMPILE=aarch64-linux-gnu-

The CROSS_COMPILE environment variable will be used as prefix for the compiler in the u-boot (and later kernel) gcc compiler.

u-boot has a default configuration for the Raspberry Pi 3 that we can load with:

	$ make rpi_3_defconfig

Now everything is ready, let's compile:

	$ make -jN

where N is the desired level of parallelism. Now we can move the bootloader in the boot partition of the SD card:

	# cp u-boot.bin /mnt/

Next: Linux kernel, Previous: u-boot, [Index]

5 boot.scr

Build and copy u-boot is not enough. u-boot is a highly configurable bootloader that requires a list of variables that need to be set in order to boot correctly. This can be done in the first boot in the u-boot console or we can do it beforehand by creatig a binary with the variables and their values. The binary file is called boot.scr. I start from creating a directory where I am going to work for it:

	$ mkdir boot.scr
	$ cd boot.scr

I will write all my desired u-boot variables in the boot.cmd file:

	$ vim boot.cmd

	mmc dev 0
	fatload mmc 0:1 ${kernel_addr_r} Image
	fatload mmc 0:1 ${fdt_addr_r} bcm2837-rpi-3-b.dtb
	setenv bootargs 8250.nr_uarts=1 bcm2708_fb.fbwidth=720 bcm2708_fb.fbheight=480 bcm2708_fb.fbswap=1 smsc95xx.macaddr=B8:27:EB:C5:35:AC vc_mem.mem_base=0x3ec00000 vc_mem.mem_size=0x40000000  root=/dev/mmcblk0p2 rootwait rw console=tty1 console=ttyS0,115200
	booti ${kernel_addr_r} - ${fdt_addr_r}

And I create the variable in the format required by u-boot. the mkimage command can be found in the uboot-tools package:

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

Finally I copy the file in the boot partition:

	# cp boot.scr /mnt/

Next: File system, Previous: boot.scr, [Index]

6 Linux Kernel

And now, time for the Linux Kernel which will be loaded by u-boot. Firt we fetch it:

	$ git clone git://
	$ cd linux

This is the mainline version of the Linux Kernel, where all the development happens. In order to compile it we need, as I did for u-boot, to declare the cross compiler:

	$ export CROSS_COMPILE=aarch64-linux-gnu-

The Linux Kernel can be loaded by a very wide range of architectures and machines and in it there is architecture specific code that will be compiled only for the ones we need. The default architecture is the one of the machine that is compiling, presumabily an x86_64. But, since we are cross compiling, we need to tell the kernel to build only the ARM 64 code.

For that we export one more variable:

	$ export ARCH=arm64

Note how kernel folks call the ARM 64 architecture arm64, while the other call it aarch64.

For arm64 there is a common kernel configuration file for all the arm64 boards. As we will see, the DTB will make the difference. Let's configure it:

	$ make defconfig

Thanks to the ARCH environment variable we set earlier, the kernel will load the default configuration for ARM64.

Now we can compile it:

	$ make -jN

Now I can copy the kernel in the boot partition of the SD card:

	# cp arch/arm64/boot/Image /mnt/

This kernel can boot any arm 64 bit machine, but it requires a set of information that are specific to the Raspberry Pi 3. Along with the Kernel before we have built the so called DTB (Device Tree Binary or Binding? boh!) that is a binary file parsed by the kernel during boot that contains all the board specific information:

	# cp arch/arm64/boot/dts/broadcom/bcm2837-rpi-3-b.dtb /mnt/

This was the last file required by Raspberry Pi 3 to boot. Now I can umount the partition and move to building the File System

	# sync
	# umount /dev/sda1

Next: Bottom, Previous: Linux Kernel, [Index]

7 File system

The file system is one of the hardest choices, there are no good or better solutions, it all depends on the specifc needs of the developer.

In the past I used to use buildroot which is a highly configurable distribution, based on KConfig, but I was struck on how much configuration it needs in order to have everything updated and by how stubborn was the community on trying to keep every build reproducable, which is the main reason why parts of it are outdated.

There is also my friend's Aaro's los that is great for its configurability, as well, but still needs some more development.

Debian is easy to setup and easy to maintain, very updated and a Debian host machine provides some great tools for maintaining it. The Debian wiki page is a good reference. I will choose Debian!

qemu-debootsrap is a debootstrap version able to execute the second stage using quemu:

	# qemu-debootstrap --arch=arm64 --keyring /usr/share/keyrings/debian-archive-keyring.gpg --variant=buildd --exclude=debfoster jessie debian-arm64

This will fetch from the repository a complete ARM debian file system. The latest qemy can allow us to chroot into it:

	# chroot debian-arm64

In the chroot environment we can use the apt to install the necessary tools, like vim, ssh, network tools, etc.

The file system should be ready to use, I copy it in the SD card; I mount it:

	# mount /dev/sda2 /mnt/

and I copy the filesystem:

	# cp -aux debian-arm64/* /mnt/

I still need to install the kernel modules:

	$ cd <path to the linux kernel>/linux
	# INSTALL_MOD_PATH=/mnt/ make modules_install

I sync and umount:

	# sync
	# umount /dev/sda2

Finally the we can insert the SD card, boot and enjoy the newly setup fully mainline Raspberry Pi 3.

Home / Index / Top / Last: File system

Andi Shyti
email: andi (at) smida (dot) it
IRC: cazzacarna