Compiling and Porting the Linux Kernel to the Beaglebone Black

Embedded Linux Development Assignment 1 (part 3)

This is Part 3 of this blog series, this is a continuation of the write-up for my assignment.

This blog is based on Bootlin's Embedded Linux Course as well as Frank Vasquez and Chris Simmonds Mastering Embedded Linux Programming, so you may have some success reading their materials alongside this blog.

In this section, we will be compiling, and configuring the Linux kernel and porting it to the Beaglebone Black.

If you haven't read Part 1 and Part 2 of this series, you may be lost.

Fetching the source code

First, clone the Linux repository into an appropriate directory.

git clone

This repository is, at the time of writing, 2.5GB, so go grab a coffee.

Now we have the Linux source code on our machine. However, these are the 'mainline' releases where we would be best off using the 'stable' releases. These are maintained in a separate repository. For more info on what this means, check out This Page on the differences.

cd linux
git remote add stable
git fetch stable

This again may take a while depending on your internet speed.

Cross-compiling the kernel

At the time of writing, remotes/stable/linux-6.1.y is the latest stable release, you can check your branches with

git branch -a

You can check which kernel version the local repository is on with

make kernel version

My output was: 6.2.0-rc5

Aside - This means Linus Torvalds is currently working on releasing Linux 6.2.0, and on Release Candidate #5. Again, check This Page to learn more about what that means.

Let's choose the newest stable branch in an attempt to stay current.

git checkout remotes/stable/linux-6.1.y

Try make kernelversion again to see the difference.

Define some environment variables so Linux's make scripts know what we want to compile for an ARM board, and know what C compiler we want to use (make sure the cross-compiling toolchain we made in Part 1 of this blog series is on the PATH).

export ARCH=arm
export CROSS_COMPILE=arm-training-linux-uclibcgnueabihf-

Run make help. Without exporting the variables above, or including them in the command, your output will look much different. This will show you all of the configurations Linux can build for. Ours for the Beaglebone is multi_v7_defconfig (Frank Vasquez and Chris Simmonds Mastering Embedded Linux Programming says to use this one, while Bootlin says to use omap2plus_defconfig, but both worked for me.)

Enter the configuration menu with make menuconfig

In this menu, find and disable CONFIG_GCC_PLUGINS. Bootlin suggests this, as it will skip building special plugins which require extra dependencies, and we don't need them. You can find the location of this using typical vim commands. Enter / to search for this option.

Now, we're ready to compile the kernel!

# replace <n> with the amount of cores your CPU has to parallelize the build
make zImage -j<n>

Now sit back and wait for a bit. This will take a while depending on your CPU speed and core count.

Once the kernel compiles, you'll need to find two files and copy them to the boot partition of the SD card.


These are the kernel image, and the device tree file respectively.

Congratulations! You've compiled the kernel from source!

Booting the Kernel

Plug in your USB to TTL cable, and start-up picocom to get a serial connection to the Beaglebone.

picocom -b 115200 /dev/ttyUSB0

We still want to boot from the SD card, rather than the bootloader on the eMMC. Be ready to press the space bar on your keyboard to avoid going through U-Boot's auto-boot sequence. Hold down the USR button, and plug in the cord to provide power to the Beaglebone.

Be sure you're greeted by your bootloader rather than the one on the eMMC. Check the date, like from Part 2 of this series, and make sure it's the date in which you compiled U-Boot.

You should see the familiar prompt => on your picocom terminal.

Once the kernel boots up, we want it to send its output to its UART pins, which are the ones our USB to TTL converter are connected. Enter the following into your picocom terminal.

=> setenv bootargs console=ttyS0,115200n8
=> saveenv

Now we're going to load the kernel image, and device tree file into memory:

=> fatload mmc 0:1 0x80200000 zImage
=> fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb

Let's boot this sucker!

=> bootz 0x80200000 - 0x80f00000

What you'll see in the output might look similar to:

[    0.000000] Booting Linux on physical CPU 0x0
[    0.000000] Linux version 6.1.7 (billsDesktop@fedora) (arm-linux-gcc (crosstool-NG 11.3.0, GNU ld (crosstool-NG 2.39) #2 SMP Mon Jan 23 20:25:30 EST 2023
[    0.000000] CPU: ARMv7 Processor [413fc082] revision 2 (ARMv7), cr=10c5387d
[    0.000000] CPU: PIPT / VIPT nonaliasing data cache, VIPT aliasing instruction cache
[    0.000000] OF: fdt: Machine model: TI AM335x BeagleBone Black


[    3.472852] Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0)
[    3.481165] CPU: 0 PID: 1 Comm: swapper/0 Not tainted 6.1.7 #2
[    3.487037] Hardware name: Generic AM33XX (Flattened Device Tree)
[    3.493177]  unwind_backtrace from show_stack+0x10/0x14
[    3.498478]  show_stack from dump_stack_lvl+0x40/0x4c
[    3.503584]  dump_stack_lvl from panic+0x108/0x33c
[    3.508434]  panic from mount_block_root+0x168/0x208
[    3.513462]  mount_block_root from prepare_namespace+0x150/0x18c
[    3.519523]  prepare_namespace from kernel_init+0x18/0x12c
[    3.525061]  kernel_init from ret_from_fork+0x14/0x2c
[    3.530156] Exception stack(0xe0009fb0 to 0xe0009ff8)
[    3.535240] 9fa0:                                     00000000 00000000 00000000 00000000
[    3.543464] 9fc0: 00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000
[    3.551684] 9fe0: 00000000 00000000 00000000 00000000 00000013 00000000
[    3.558350] ---[ end Kernel panic - not syncing: VFS: Unable to mount root fs on unknown-block(0,0) ]---

This last section might be problematic for someone not in our shoes, but for us, this Kernel Panic is mentioning that we don't have a root file system mounted. That's OK, we'll get to that in the next blog series.

For now, let's be happy we've booted a cross-compiled Linux kernel on the Beaglebone!

Automate this process with a simple bootscript:

=> setenv bootcmd 'fatload mmc 0:1 0x80200000 zImage; fatload mmc 0:1 0x80f00000 am335x-boneblack.dtb; bootz 0x80200000 - 0x80f00000'
=> saveenv