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.
Fetching the source code
First, clone the Linux repository into an appropriate directory.
git clone https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux
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 https://git.kernel.org/pub/scm/linux/kernel/git/stable/linux 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:
Let's choose the newest stable branch in an attempt to stay current.
git checkout remotes/stable/linux-6.1.y
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-
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
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 22.214.171.124_7622b49) 11.3.0, GNU ld (crosstool-NG 126.96.36.199_7622b49) 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