Reader small image

You're reading from  Linux Kernel Programming - Second Edition

Product typeBook
Published inFeb 2024
PublisherPackt
ISBN-139781803232225
Edition2nd Edition
Tools
Right arrow
Author (1)
Kaiwan N. Billimoria
Kaiwan N. Billimoria
author image
Kaiwan N. Billimoria

Kaiwan N. Billimoria taught himself BASIC programming on his dad's IBM PC back in 1983. He was programming in C and Assembly on DOS until he discovered the joys of Unix, and by around 1997, Linux! Kaiwan has worked on many aspects of the Linux system programming stack, including Bash scripting, system programming in C, kernel internals, device drivers, and embedded Linux work. He has actively worked on several commercial/FOSS projects. His contributions include drivers to the mainline Linux OS and many smaller projects hosted on GitHub. His Linux passion feeds well into his passion for teaching these topics to engineers, which he has done for well over two decades now. He's also the author of Hands-On System Programming with Linux, Linux Kernel Programming (and its Part 2 book) and Linux Kernel Debugging. It doesn't hurt that he is a recreational ultrarunner too.
Read more about Kaiwan N. Billimoria

Right arrow

Building the 6.x Linux Kernel from Source – Part 2

This chapter continues from where the previous chapter left off. In the previous chapter, in the Steps to build the kernel from source section, we covered the first three steps of building our kernel. There, you learned how to download and extract the kernel source tree or even use git clone to get one (steps 1 and 2). We then proceeded to understand the kernel source tree layout, and, very importantly, the various approaches to correctly arrive at a starting point to configure the kernel (step 3). We even added a custom menu item to the kernel configuration menu.

In this chapter, we will continue our quest to build the kernel by covering the remaining four steps to build it. First, of course, we will build it (step 4). You will then learn how to properly install the kernel modules that get generated as part of the build (step 5). Next, we will run a simple command that sets up the GRUB (Grand Unified Bootloader) bootloader...

Technical requirements

Before we begin, I assume that you have downloaded, extracted (if required), and configured the kernel, thus having a .config file ready. If you haven’t already, please refer to Chapter 2, Building the 6.x Linux Kernel from Source – Part 1, for the details on how exactly this is done. We can now proceed to build it.

Step 4 – building the kernel image and modules

Performing the build from the end user’s point of view is straightforward. In its simplest form, just ensure you’re at the root of the configured kernel source tree and type make. That’s it – the kernel image and any kernel modules (and, on an embedded system, possibly a Device Tree Blob (DTB) binary) will get built. Grab a coffee! The first time around, it could take a while.

Of course, there are various Makefile targets we can pass to make. A quick make help command issued on the command line reveals quite a bit. Remember, we used this earlier, in fact, to see all possible configuration targets (revisit Chapter 2, Building the 6.x Kernel from Source – Part 1, and particularly the Seeing all available config options section if you’d like). Here, we use it to see what gets built by default with the all target:

$ cd ${LKP_KSRC}     # recall that the env var LKP_KSRC holds the pathname...

Step 5 – installing the kernel modules

In the previous step, all the kernel config options that were marked as m – in effect, all the kernel modules, the *.ko files – have by now been built within the source tree. As you shall learn, that’s not quite enough: they must now be installed into a known location on the system. This section covers these details.

Locating the kernel modules within the kernel source

As you just learned, the previous step – building the kernel image and modules – resulted in the compressed and uncompressed kernel images being generated, as well as all the kernel modules (as specified by our kernel config). Kernel modules are identified as files that always have a .ko (for kernel object) suffix. These modules are very useful; they give us kernel functionality in a modular manner (we can decide to plug them in or out of kernel memory at will; the following two chapters will go into great detail on the topic).

...

Step 6 – generating the initramfs image and bootloader setup

Firstly, please note that this discussion is highly biased toward the x86[_64] architecture, perhaps the most common one in use. Nevertheless, the concepts learned here can be directly applied to other architectures (like ARM), though the precise commands may vary. Typically, unlike on the x86, and at least for ARM-based Linux, there’s no direct command to generate the initramfs image; it has to be done manually, “by hand.” Embedded builder projects like Yocto and Buildroot do provide ways to automate this.

For the typical x86 desktop or server kernel build procedure, this step is internally divided into two distinct parts:

  • Generating the initramfs (formerly called initrd) image
  • GRUB setup for the new kernel image

The reason it’s encapsulated in a single step here is that, on the x86 architecture, convenience scripts perform both tasks, giving the appearance...

Understanding the initramfs framework

A bit of a mystery remains! What exactly is this initramfs (initial RAM filesystem) or initrd (initial RAM disk) image for? Why is it there?

Firstly, using this feature is a choice – the kernel config directive is called CONFIG_BLK_DEV_INITRD. It’s set to y and hence on by default. In brief, for systems that do not know in advance certain things such as the boot disk host adapter or controller type (SCSI, RAID, and so on), the exact filesystem type that the root filesystem is formatted as (is it ext2, ext4, btrfs, f2fs, or something else?), or for those systems where these functionalities are always built as kernel modules, we require the initramfs capability. Exactly why will become clear in a moment. Also, as mentioned earlier, initrd is now considered an older term. Nowadays, we more often use the term initramfs in its place.

But what exactly is the difference between the older initrd and newer initramfs? The key difference...

Step 7 – customizing the GRUB bootloader

We have now completed steps 1 to 6 as outlined in Chapter 2, Building the 6.x Linux Kernel from Source – Part 1, in the Steps to build the kernel from source section. You can now reboot the system; of course, do first save and close all your apps and files. By default, though, the modern GRUB does not even show us any menu on reboot; it will by default boot into the newly built kernel (do remember that, here, we’re describing this process only for x86[_64] systems running Ubuntu; the default kernel booted into can also vary with the distro).

On x86[_64] you can always get to the GRUB menu during early system boot. Just ensure you keep the Shift key pressed down during boot. Again, this behavior does depend on other factors – on systems with newer UEFI/BIOS firmware enabled, or when running within a nested VM, you may require other ways to force-see the GRUB menu at boot (try pressing Esc too).

...

Verifying our new kernel’s configuration

Okay, so back to our discussion. We have now booted into our newly built kernel. But hang on, please don’t blindly assume that everything’s just fine; let’s actually verify that.

The empirical approach is always best; in this section, let’s verify that we are indeed running the (6.1.25) kernel we just built and that it has indeed been configured as we intended. We start by examining the kernel version:

$ uname -r
6.1.25-lkp-kernel

Indeed, we are now running Ubuntu 22.04 LTS on our just-custom-built 6.1.25 LTS kernel! Further variations of the uname utility show us the machine hardware name and the OS is as planned: we’re on the x86_64 running GNU/Linux:

$ uname -m ; uname -o
x86_64
GNU/Linux

Moving along, recall our discussion in Chapter 2, Building the 6.x Linux Kernel from Source – Part 1, in the Sample usage of the make menuconfig UI section, where we performed a couple...

Kernel build for the Raspberry Pi

A popular and relatively inexpensive Single-Board Computer (SBC) to experiment and prototype with is the ARM-based Raspberry Pi. Hobbyists, tinkerers, and, to some extent, even pros find it very useful to try out and learn how to work with embedded Linux, especially as it has a strong community backing (with many Q&A forums) and excellent support. (You’ll find a brief discussion and picture of a Raspberry Pi board in Online Chapter, Kernel Workspace Setup, in the Experimenting with the Raspberry Pi section. By the way, there are several well-known Raspberry Pi clones – like the Orange Pi – that work very well; the discussions should apply equally to them.)

There are two typical ways in which you can build a kernel for the target device or Device Under Test (DUT) that we’ll use here, the Raspberry Pi 4 Model B (64-bit):

  • Build the kernel on a powerful host system, typically an x86_64 (or Mac) desktop or...

Miscellaneous tips on the kernel build

We complete this chapter on building the Linux kernel from source with a few tips. The reason, of course, is that everything may not always go as smoothly as we’d wish it to. Each of the following subsections encapsulates a tip for you to take note of.

Often a point of confusion for folks new to this: once we configure, build, and boot from a new Linux kernel, we notice that the root filesystem and any other mounted filesystems remain identical to what was on the original (distro or custom) system. Only the kernel (and the initramfs image) has changed.

This is entirely intentional, due to the Unix paradigm of having loose coupling between the kernel and the (real) root filesystem. Since it’s the root filesystem that holds all the applications, system tools, and utilities, including libraries, in effect, we can have several kernels (to suit different product flavors, perhaps) for the same base system.

Minimum version...

Summary

This chapter, along with the previous one, covered in a lot of detail the necessary preliminaries and how exactly to configure and build the Linux kernel from source.

In this chapter, we began with the actual kernel (and kernel modules’) build process. Once built, we showed how the kernel modules are to be installed onto the system. We then moved on to the practicalities of generating the initramfs (or initrd) image and went on to explain the motivation behind it.

The final step in the kernel build was the (simple) customization of the bootloader (here, we focused only on x86 GRUB). We then showed how to boot the system via the newly baked kernel and verify that its configuration is as we expected. As a useful add-on, we then showed how we can even cross-compile the Linux kernel for another processor (AArch64, in this instance). Finally, we shared some additional tips to help you with the kernel build.

Again, if you haven’t done so already, we urge...

Questions

As we conclude, here is a list of questions for you to test your knowledge regarding this chapter’s material: https://github.com/PacktPublishing/Linux-Kernel-Programming_2E/blob/main/questions/ch3_qs_assignments.txt. You will find some of the questions answered in the book’s GitHub repo: https://github.com/PacktPublishing/Linux-Kernel-Programming/tree/master/solutions_to_assgn.

Further reading

To help you delve deeper into the subject with useful materials, we provide a rather detailed list of online references and links (and at times, even books) in a Further reading document in this book’s GitHub repository. The Further reading document is available here: https://github.com/PacktPublishing/Linux-Kernel-Programming/blob/master/Further_Reading.md.

Learn more on Discord

To join the Discord community for this book – where you can share feedback, ask questions to the author, and learn about new releases – follow the QR code below:

https://packt.link/SecNet

lock icon
The rest of the chapter is locked
You have been reading a chapter from
Linux Kernel Programming - Second Edition
Published in: Feb 2024Publisher: PacktISBN-13: 9781803232225
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
undefined
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $15.99/month. Cancel anytime

Author (1)

author image
Kaiwan N. Billimoria

Kaiwan N. Billimoria taught himself BASIC programming on his dad's IBM PC back in 1983. He was programming in C and Assembly on DOS until he discovered the joys of Unix, and by around 1997, Linux! Kaiwan has worked on many aspects of the Linux system programming stack, including Bash scripting, system programming in C, kernel internals, device drivers, and embedded Linux work. He has actively worked on several commercial/FOSS projects. His contributions include drivers to the mainline Linux OS and many smaller projects hosted on GitHub. His Linux passion feeds well into his passion for teaching these topics to engineers, which he has done for well over two decades now. He's also the author of Hands-On System Programming with Linux, Linux Kernel Programming (and its Part 2 book) and Linux Kernel Debugging. It doesn't hurt that he is a recreational ultrarunner too.
Read more about Kaiwan N. Billimoria