Getting started with Embedded Linux: Part Five
Author's Note: This series on how to get started using Embedded Linux is on my Open Mike blog beginning with installing Linux on a virtual machine, learning the basics of Linux, an introduction to programming on Linux, and a brief look at installing the wide range of packages available for Linux. Our goal is not to turn you into an overnight expert, but rather to give someone with experience in embedded systems a road map to extending that experience to using Linux.
Most embedded projects will require configuring the Linux kernel and perhaps developing device drivers to match your hardware. Since the same kernel is used in our desktop Linux system running in a virtual machine as you would use on your embedded system, we can build a custom kernel and a simple driver on the desktop system in much the same way we will with a development board.
Each of the major distributions describes how they build a kernel. For Fedora, the description is on the Fedora Wiki and for Ubuntu it is available on the community site. Each of these will install the source used to build your VM system. They use programs like rpmbuild or scripts to make the process a bit easier, although perhaps not as transparent as it might be.
I recommend that you log into your VM and follow the scripts to build an install a new kernel. Before you install, you might take a snapshot of the VM just in case things go haywire and you want to get back to a known stable system.
When you follow these instructions, they will created a directory containing the kernel used to build the release you have installed. This is the “vanilla” kernel, which can be downloaded from kernel.org, plus a number of patches selected by the distribution. If you are using Fedora, as I am, you can find both the vanilla and patched kernels in ~/rpmbuild/BUILD/kernel-
$ cp -r ~/rpmbuild/BUILD/kernel*/linux* ~/linux
Older guides will frequently say to copy the kernel to /usr/src/linux, which is the traditional location for the kernel. You don't need to do this; the kernel can be built in any location. And usually /usr/src is a protected directory, only writable by root. You do not need to be root to build the kernel.
Let's take a brief tour of a few of the files and subdirectories under ~/linux:
Makefile. Use to control configuring and building the kernel
Arch. Contains architecture specific files
Drivers. Sources for drivers included with the kernel
Include. Headers used to build the kernel and drivers
Kernel. The “core” target-independent parts of the kernel
README. A description of the directory, with make directives
Scripts. Bash scripts used to build the kernel and drivers
There are several directories which contain parts of the kernel, like net (networking), mm (memory management), fs (file system), etc. And there's lots of documentation as well.
The arch directory contains sub-directories for more than two dozen different processor architectures, including the very popular ARM and Intel x86 processors and several which are much less well known. These directories contain the target-specific code which allows Linux to run on so many different processors. You'll find directories with the same names as under kernel which contain the target-specific code to support the target-independent functions.
Central to building the Linux kernel is the .config file. This is a hidden file (the initial dot says to hide it, but you can see it if you enter “ls -a”.) This is the current configuration. There are sample config files under the configs directory. X86 has only two, one for 32-bit and the other for 64-bit. ARM has dozens for many different processor configurations. If you open arch/x86/configs/i386_defconfig, you will see something like this:
# CONFIG_LOCALVERSION_AUTO is not set
Each of the y's says that an option is enabled. A configuration option which is commented out or missing, is not enabled.
It would be a daunting task to create one of these configuration files from scratch, and happily, we don't have to. The sources you downloaded for your VM included a .config, or perhaps has instructions on copying the config file corresponding to your installation from the /boot directory. To get started building the kernel, you run
$ make oldconfig
If you want to see all of the options (and select which you want to enable or disable using a simple text-based utility, run
$ make menuconfig
Go ahead and explore the GUI that is then displayed (Figure 1 below). The arrow keys allow you to move up or down the list. To set or unset an option, press the space bar. To go to a sub-menu (indicated by an -> arrow), press Enter. The left and right arrows select the options on the bottom of the window. Selecting Help will tell you a bit about the option. When you want to exit a sub-menu, select Exit. At the top-level menu, select Save to save your changes to .config.
Figure 1. Linux graphical user interface.
Before we build our kernel, let's give it a (slightly) different name. If you open the Makefile in an editor, the first several lines will have the VERSION, PATCHLEVEL, SUBLEVEL, and EXTRAVERSION values specified. The first three will be the same as the kernel version. In my case, this was 3.11.9, the same values as on the name of the kernel source RPM I downloaded. Add your name or a number to .config
This will be used to make the newly built kernel different from the installed kernel.
To build the kernel, it's a good idea to start with a clean slate:
$ make clean
You don't need to do this if you wish to continue a build which stopped, perhaps because of a compile failure.
Compiling the kernel takes quite a bit of time, even on a fast system:
Only one line is printed for each compilation. If you want details (lots of details), add V=1 at the end of the make command.
Next, you need to build the loadable modules which were not linked into the kernel. This is much quicker:
$ make modules
Install the modules under /lib, then install the new kernel in /boot. These two commands need to be executed as root, since they modify protected system directories:
# make modules_install
# make install
Take a look at /lib/modules and /boot to see what the Makefile installed. You can now reboot your VM:
Select your new kernel from the GRUB boot menu. Once the system reboots and you log in, you can check the version by running “uname -1”. You should see the version number of the new kernel, including your name or the number you added to EXTRAVERSION.
You might modify the configuration, rebuild and install the kernel. Update the EXTRAVERSION each time to help keep track of which kernel is which. Don't worry if you create a kernel which crashes. You can always select one of the older versions in the GRUB boot menu, or, if you took my advice to save a snapshot of the VM, you can restore from the snapshot and pick up where that left off.
There are some differences when building the Linux kernel for Embedded Linux, so we will revisit this later in the series. But the file organization, configuration, and overall process for building the Linux kernel is similar for a desktop Linux system and an Embedded Linux system.
Next time we will talk about building loadable modules, commonly used for device drivers.
Michael Eager is principal consultant at Eager Consulting in Palo Alto, Calif. He has over four decades experience developing compilers, debuggers, and simulators for a wide range of processor architectures used in embedded systems. His current and former clients include major semiconductor companies and systems developers. Michael has been a member of the ISO C++ Standard Committee and ABI Committees for several processor architectures. He is chair of the Debugging Standards Committee for DWARF, a widely used debug data format. He is active in the open-source and Linux communities.Related links:
- Learning Linux for embedded systems
- Getting started with Embedded Linux: Part Two
- Getting started with Embedded Linux: Part Three
- Getting started with Embedded Linux: Part Four
- Getting started with Embedded Linux: Part Five
- Getting started with Embedded Linux: Part Six
- Getting started with Embedded Linux: Part Seven