Build a Raspberry Pi Linux Kernel for QEMU

Compile the Kernel

Make a working directory:

1
2
mkdir /tmp/rpi-linux
cd /tmp/rpi-linux

Get the RPI tools and kernel:

1
2
git clone https://github.com/raspberrypi/tools
git clone https://github.com/raspberrypi/linux

Point to the cross compiler (N.B. there’s a dash “-“ at the end!):

1
export CCPREFIX=/tmp/rpi-linux/tools/arm-bcm2708/gcc-linaro-arm-linux-gnueabihf-raspbian/bin/arm-linux-gnueabihf-

Save the following patch as linux-rpi-qemu.patch:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
diff --git a/arch/arm/mach-versatile/Kconfig b/arch/arm/mach-versatile/Kconfig
index 1dba368..82fa543 100644
--- a/arch/arm/mach-versatile/Kconfig
+++ b/arch/arm/mach-versatile/Kconfig
@@ -4,7 +4,6 @@ menu "Versatile platform type"
config ARCH_VERSATILE_PB
bool "Support Versatile Platform Baseboard for ARM926EJ-S"
default y
- select CPU_ARM926T
select MIGHT_HAVE_PCI
help
Include support for the ARM(R) Versatile Platform Baseboard
@@ -12,7 +11,6 @@ config ARCH_VERSATILE_PB
config MACH_VERSATILE_AB
bool "Support Versatile Application Baseboard for ARM926EJ-S"
- select CPU_ARM926T
help
Include support for the ARM(R) Versatile Application Baseboard
for the ARM926EJ-S.
diff --git a/arch/arm/mm/Kconfig b/arch/arm/mm/Kconfig
index bb263d8..f1fbc63 100644
--- a/arch/arm/mm/Kconfig
+++ b/arch/arm/mm/Kconfig
@@ -71,7 +71,7 @@ config CPU_ARM9TDMI
# ARM920T
config CPU_ARM920T
- bool "Support ARM920T processor" if (ARCH_MULTI_V4T && ARCH_INTEGRATOR)
+ bool "Support ARM920T processor" if (ARCH_MULTI_V4T && ARCH_INTEGRATOR) || ARCH_VERSATILE_PB || ARCH_VERSATILE_AB
select CPU_32v4T
select CPU_ABRT_EV4T
select CPU_CACHE_V4WT
@@ -89,7 +89,7 @@ config CPU_ARM920T
# ARM922T
config CPU_ARM922T
- bool "Support ARM922T processor" if (ARCH_MULTI_V4T && ARCH_INTEGRATOR)
+ bool "Support ARM922T processor" if (ARCH_MULTI_V4T && ARCH_INTEGRATOR) || ARCH_VERSATILE_PB || ARCH_VERSATILE_AB
select CPU_32v4T
select CPU_ABRT_EV4T
select CPU_CACHE_V4WT
@@ -127,7 +127,7 @@ config CPU_ARM925T
# ARM926T
config CPU_ARM926T
- bool "Support ARM926T processor" if (!ARCH_MULTIPLATFORM || ARCH_MULTI_V5) && (ARCH_INTEGRATOR || MACH_REALVIEW_EB)
+ bool "Support ARM926T processor" if ((!ARCH_MULTIPLATFORM || ARCH_MULTI_V5) && (ARCH_INTEGRATOR || MACH_REALVIEW_EB)) || ARCH_VERSATILE_PB || ARCH_VERSATILE_AB
select CPU_32v5
select CPU_ABRT_EV5TJ
select CPU_CACHE_VIVT
@@ -135,6 +135,7 @@ config CPU_ARM926T
select CPU_CP15_MMU
select CPU_PABRT_LEGACY
select CPU_TLB_V4WBI if MMU
+ depends on !CPU_V6 && !CPU_V7
help
This is a variant of the ARM920. It has slightly different
instruction sequences for cache and TLB operations. Curiously,
@@ -358,7 +359,7 @@ config CPU_PJ4B
# ARMv6
config CPU_V6
- bool "Support ARM V6 processor" if (!ARCH_MULTIPLATFORM || ARCH_MULTI_V6) && (ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX || MACH_BCM2708)
+ bool "Support ARM V6 processor" if ((!ARCH_MULTIPLATFORM || ARCH_MULTI_V6) && (ARCH_INTEGRATOR || MACH_REALVIEW_EB || MACH_REALVIEW_PBX || MACH_BCM2708)) || ARCH_VERSATILE_PB || ARCH_VERSATILE_AB
select CPU_32v6
select CPU_ABRT_EV6
select CPU_CACHE_V6
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index 1af139a..fd51e26 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -6,7 +6,7 @@ comment "MMC/SD/SDIO Host Controller Drivers"
config MMC_BCM2835
tristate "MMC support on BCM2835"
- depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835
+ depends on MACH_BCM2708 || MACH_BCM2709 || ARCH_BCM2835 || ARCH_VERSATILE_PB || ARCH_VERSATILE_AB
help
This selects the MMC Interface on BCM2835.

And apply it to the kernel:

1
2
cd linux/
patch -p1 < ../linux-rpi-qemu.patch

Now we’re ready to configure the kernel:

1
2
make ARCH=arm CROSS_COMPILE=${CCPREFIX} versatile_defconfig
make ARCH=arm CROSS_COMPILE=${CCPREFIX} menuconfig

In the menu config, select the following options. Make sure they are starred [*] so that they are not built as modules [m].

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
System type--->
[*] Support ARM V6 processor
[*] ARM errata: Invalidation of the Instruction Cache operation can fail
[*] ARM errata: Possible cache data corruption with hit-under-miss enabled
Floating point emulation --->
[*] VFP-format floating point maths
Kernel Features --->
[*] Use the ARM EABI to compile the kernel
[*] Allow old ABI binaries to run with this kernel (EXPERIMENTAL)
Bus support --->
[*] PCI support
Device Drivers --->
SCSI device support --->
<*> SCSI device support
<*> SCSI disk support
<*> SCSI CDROM support
[*] SCSI low-level drivers (NEW) --->
<*> SYM53C8XX Version 2 SCSI support
Device Drivers --->
<*> MMC/SD/SDIO card support --->
<*> MMC support on BCM2835
Device Drivers --->
Generic Driver Options --->
[*] Maintain a devtmpfs filesystem to mount at /dev
[*] Automount devtmpfs at /dev, after the kernel mounted the rootfs
File systems --->
<*> Ext3 journalling file system support
<*> The Extended 4 (ext4) filesystem
File systems --->
Pseudo filesystems --->
[*] Tmpfs virtual memory file system support (former shm fs)

Build the kernel:

1
make ARCH=arm CROSS_COMPILE=${CCPREFIX} -j4

Compile BusyBox

Now let’s build BusyBox so that we have something once we are booted:

1
2
3
4
5
6
cd ..
wget http://busybox.net/downloads/busybox-1.23.2.tar.bz2
tar xf busybox-1.23.2.tar.bz2
cd busybox-1.23.2/
make CROSS_COMPILE=${CCPREFIX} defconfig
make CROSS_COMPILE=${CCPREFIX} menuconfig

We want to build BusyBox statically, so select this option:

1
2
3
Busybox Settings --->
Build Options --->
[*] Build BusyBox as a static binary (no shared libs)

Compile BusyBox:

1
2
make CROSS_COMPILE=${CCPREFIX} -j4
make CROSS_COMPILE=${CCPREFIX} install

We can now make our initramfs containing BusyBox:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
mkdir ../initramfs
cd ../linux
make ARCH=arm CROSS_COMPILE=${CCPREFIX} INSTALL_MOD_PATH=../initramfs modules_install
cd ../initramfs
mkdir -pv {bin,sbin,etc,proc,sys,usr/{bin,sbin}}
cp -a ../busybox-1.23.2/_install/* .
cat << EOF > init
#!/bin/sh
mount -t proc none /proc
mount -t sysfs none /sys
mount -t devtmpfs none /dev
exec /bin/sh
EOF
chmod +x init
find . -print0 | cpio --null -ov --format=newc | gzip > ../initramfs.gz

Emulating with QEMU

The kernel and the initramfs are now built, so it’s time to boot with QEMU:

1
2
cd ..
qemu-system-arm -M versatilepb -cpu arm1176 -kernel linux/arch/arm/boot/zImage -initrd initramfs.gz -append "console=ttyAMA0" -nographic

References

https://www.raspberrypi.org/documentation/linux/kernel/building.md
http://elinux.org/Raspberry_Pi_Kernel_Compilation
https://github.com/cantora/qemu-arm-rpi-kernel
http://tiriboy.blogspot.com/2015/04/compiling-arm1176-for-qemu-raspberry-pi.html
http://mgalgs.github.io/2015/05/16/how-to-build-a-custom-linux-kernel-for-qemu-2015-edition.html
https://balau82.wordpress.com/2012/03/31/compile-linux-kernel-3-2-for-arm-and-emulate-with-qemu/