From Fedora Project Wiki

This patch contains instructions for building a kernel from the source RPM. Make sure you have installed all dependencies

Consider reading the walkthrough which was written at the time of fc36 before reading the instructions which seem to have been written at the time of fc16.

Get the Source

Do Not Build Packages as root.
Configuring and building packages as root is inherently dangerous and not required, even for the kernel. The following instructions allow any normal user to configure and build kernels from the source packages.

If you've never built an RPM before, prepare an RPM package-building environment in your (non-root) home directory by running the following command:

$ rpmdev-setuptree

This command creates a new directory ~/rpmbuild, with several empty subdirectories including SPECS, SOURCES, BUILD and others. It also creates an initial ~/.rpmmacros customization file for you. This only needs to be done once.

Download the kernel-<version>.src.rpm file:

$ koji download-build --arch=src kernel-<version>

Install kernel-<version>.src.rpm with the following command:

$ rpm -Uvh kernel-<version>.src.rpm

This command writes the RPM contents into ${HOME}/rpmbuild/SOURCES and ${HOME}/rpmbuild/SPECS, where ${HOME} is your home directory. It is safe to ignore any messages similar to the following:

warning: user kojibuilder does not exist - using root
warning: group kojibuilder does not exist - using root
Space Required. The full kernel building process requires several gigabytes of extra space on the file system containing your home directory.

Copy the Source Tree and Generate a Patch

This step is for applying a patch to the kernel source. If a patch is not needed, proceed to "Configure Kernel Options".

Advanced Users
There are tools such as 'quilt' that allow you to avoid copying the source tree. For advanced users, such tools are often a time saver over the steps below

Copy the source tree to preserve the original tree while making changes to the


export arch=x86_64 # replace x86_64 with your arch
export ver=3.7 # replace 3.1 with your kernel version
export subver=4-204
export fedver=fc16 # replace fc16 with your fedora version 
cp -r ~/rpmbuild/BUILD/kernel-$ver.$subver.$fedver ~/rpmbuild/BUILD/kernel-$ver.$fedver.orig
cp -al ~/rpmbuild/BUILD/kernel-$ver.$subver.$fedver.orig ~/rpmbuild/BUILD/kernel-$ver.$fedver.new
The second cp command hardlinks the .orig and .new trees to make diff run faster. Most text editors know how to break the hardlink correctly to avoid problems. Vim does not break hard links by default, so don't use 'cp -al' use 'cp -r', or look into the 'breakhardlink' setting.

Using vim on FC14, it treated the hard link as a hard link and thus the above technique failed. It was necessary to repeat the original copy used for the .orig directory for the .new directory. Note that this uses twice the space.

Make changes directly to the code in the .new source tree, or copy in a modified file. This file might come from a developer who has requested a test, from the upstream kernel sources, or from a different distribution.

After the .new source tree is modified, generate a patch. To generate the patch, run diff against the entire .new and .orig source trees with the following command:

cd ~/rpmbuild/BUILD
diff -uNrp kernel-$ver.$subver.$fedver.orig kernel-$ver.$subver.$fedver.new > ../SOURCES/linux-$ver.$subver.$fedver-mynewpatch.patch

Replace 'linux-$ver.$subver.$fedver-mynewpatch.patch' with the desired name for the new patch.

Lastly edit the patch file to remove the leading directory, this is required because the kernel spec file applies patches with '-p1' only.

For example the following:

--- kernel-3.8.fc18.orig/linux-3.8.11-200.bz708406.fc18.x86_64/kernel/kexec.c        2013-02-18 18:58:34.000000000 -0500
+++ kernel-3.8.fc18.new/linux-3.8.11-200.bz708406.fc18.x86_64/kernel/kexec.c    1969-12-31 19:00:00.000000000 -0500

Should be changed to:

--- linux-3.8.11-200.bz708406.fc18.x86_64/kernel/kexec.c        2013-02-18 18:58:34.000000000 -0500
+++ linux-3.8.11-200.bz708406.fc18.x86_64/kernel/kexec.c    1969-12-31 19:00:00.000000000 -0500


For more information on patching refer to the man pages for diff(1) and patch(1).

Configure Kernel Options

This step is for modifying the options the kernel is configured with. This step is optional. If no configuration changes are needed, proceed to "Prepare Build Files".

Small changes
If you only want to make a small number of configuration changes, you should simply set the options as desired in the config-local file. This will be sourced and override the remaining config-* files and avoids a lot of unnecessary work. You can skip the steps below if you use config-local
  1. Change to the kernel source tree directory:
    cd ~/rpmbuild/BUILD/kernel-$ver.$fedver/linux-$ver.$subver.$fedver.$arch/
    If you only want to make minor changes to the default fedora kernel, skip to step 4., and use one of the two configuration tools to edit those minor changes into the default config file.
  2. Select the desired configuration file from ~/rpmbuild/BUILD/kernel-$ver.$fedver/linux-$ver.$subver.$fedver.$arch/configs. Copy the desired config file to ~/rpmbuild/BUILD/kernel-$ver.$fedver/linux-$ver.$subver.$fedver.$arch/.config:
    cp configs/<desired-config-file> .config
  3. Run the following command:
    make oldconfig
  4. Then run the following command, selecting and saving the desired kernel options from the text-based UI:
    make menuconfig
    • For a graphical UI, instead run:
      make xconfig
  5. Add a new line to the top of the config file that contains the hardware platform the kernel is built for (the output of uname -i). The line is preceded by a # sign. For example, an x86_64 machine would have the following line added to the top of the config file:
    # x86_64
  6. Copy the config file to ~/rpmbuild/SOURCES/:
    cp .config ~/rpmbuild/SOURCES/config-`uname -m`-generic
32-bit x86 kernels
The 32-bit PAE kernel uses the config-i686-PAE configuration file. If you are building a PAE kernel, you will need to copy your config file to ~/rpmbuild/SOURCES/:
 cp .config ~/rpmbuild/SOURCES/config-i686-PAE
If you are building a non-PAE kernel, you will need to copy your config file to
 cp .config ~/rpmbuild/SOURCES/config-x86-32-generic
. Again, the use of config-local is encouraged unless you are making a large number of configuration changes.

Prepare Build Files

This step makes the necessary changes to the kernel.spec file. This step is required for building a custom kernel.

1. Change to the ~/rpmbuild/SPECS directory:

$ cd ~/rpmbuild/SPECS

2. Open the kernel.spec file for editing.
3. Give the kernel a unique name. This is important to ensure the custom kernel is not confused with any released kernel. Add a unique string to the kernel name by changing the 'buildid' line. Optionally, change ".local" to your initials, a bug number, the date or any other unique string.

Change this line: # define buildid .local

To this (note the extra space is removed in addition to the pound sign): %define buildid .<custom_text>

4. If you generated a patch, add the patch to the kernel.spec file, preferably at the end of all the existing patches and clearly commented.

# cputime accounting is broken, revert to 2.6.22 version
Patch2220: linux-2.6-cputime-fix-accounting.patch

Patch9999: linux-2.6-samfw-test.patch

Build the New Kernel

This step actually generates the kernel RPM files. This step is required for building a custom kernel.

Use the rpmbuild utility to build the new kernel:

  • Ensure /usr/sbin is in your path (to pull in /usr/sbin/modinfo):

$ export PATH=/usr/sbin:$PATH

  • To build all kernel flavors:
$ rpmbuild -bb --target=`uname -m` kernel.spec
  • To disable specific kernel flavors from the build (for a faster build):
$ rpmbuild -bb --without <option> --target=`uname -m` kernel.spec

Valid values for "option" above include debug and debuginfo. Specifying --without debug strips out some debugging code from the kernels, where specifying --without debuginfo disables the building of the kernel-debuginfo packages.

  • To specify that only a specific kernel should be built:
$ rpmbuild -bb --with <option> --target=`uname -m` kernel.spec

Valid values for "option" above include baseonly.

  • For example, to build just the kernel and kernel-devel packages, the command would be:
$ rpmbuild -bb --with baseonly --without debuginfo --target=`uname -m` kernel.spec

The build process takes a long time to complete. A lot of messages will be printed to the screen. These messages can be ignored, unless the build ends with an error. If the build completes successfully, the new kernel packages will be located in the ~/rpmbuild/RPMS directory.

TO DO
add a troubleshooting section

Following Generic Textbooks

Many of the tutorials, examples, and textbooks about Linux kernel development assume the kernel sources are installed under the /usr/src/linux/ directory. If you make a symbolic link, as shown below, you should be able to use those learning materials with the Fedora packages. Install the appropriate kernel sources, as shown earlier, and then run the following command:

su -c 'ln -s /usr/src/kernels/<version>.<release>-<arch> /usr/src/linux'

Enter the root password when prompted.

Install the New Kernel

This step actually installs the new kernel into the running system.

To install the new kernel, use the rpm -ivh command, not the -U or --upgrade options:

su -c "rpm -ivh $HOME/rpmbuild/RPMS/<arch>/kernel-<version>.<arch>.rpm"

This command will install your kernel in /boot, create a new initramfs to bootstrap your kernel, and automatically add your new kernel to your grub bootloader "menu.lst". At this point, you can reboot to give control to your new kernel.

Walkthrough

The walkthrough below has three parts. In Part 0 we prepare the system. In Part 1 we compile and run a kernel without changes. In Part 2 we make a minor change before we compile and run the kernel.

The walkthrough consists of concrete commands which have been tested by actual copy-paste to and execution in a terminal window. The downside of that is that the walkthrough considers one, particular version of one, particular kernel (kernel-5.19.4-200.fc36 in this case). Please substitute that by whatever kernel you would like to work with.

The approach taken below is not in any way a recommended workflow. It just shows how one can compile a kernel. There is plenty of room for cutting corners and improving efficiency.

# PART 0: PREPARATIONS

# The following steps are taken from
# https://fedoraproject.org/wiki/Building_a_custom_kernel

# Install some packages we need badly
sudo dnf install fedpkg fedora-packager rpmdevtools ncurses-devel pesign grubby

# Install some packages we need to run "make xconfig" in PART 2.
sudo dnf install qt5-qtbase-devel libXi-devel gcc-c++

# Install ccache so that subsequent builds may go faster than first build 
sudo dnf install ccache

# We need more packages, but we install them later in PART 1 using
# sudo dnf builddep kernel.spec

# Authorize yourself
sudo bash -c "echo $USER >> /etc/pesign/users"
sudo /usr/libexec/pesign/pesign-authorize



# PART 1: COMPILE A KERNEL WITH NO CHANGES

# If ~/rpmbuild exists, move it away since we are going to nuke ~/rpmbuild
# in a moment
mv ~/rpmbuild ~/rpmbuild-backup

# Make a fresh ~/rpmbuild
rpmdev-setuptree

# Make a directory for our temporary files
mkdir ~/mydevel

# Make a directory for the source rpm
mkdir ~/mydevel/src

# Find out the name of the kernel we run
uname -r
# In my case, the response was 5.19.4-200.fc36.x86_64
# So the name we need is 5.19.4-200.fc36

# Download the kernel source rpm
cd ~/mydevel/src
koji download-build --arch=src kernel-5.19.4-200.fc36

# Unpack the kernel source rpm into ~/rpmbuild
# There is no need for "cd ~/mydevel/src", but I include it to emphasize
# where we are in the file system.
cd ~/mydevel/src
rpm -Uvh kernel-5.19.4-200.fc36.src.rpm

# Ignore warnings like these:
# warning: user kojibuilder does not exist - using root
# warning: group kojibuilder does not exist - using root

# Install the remaining dependencies mentioned in PART 0
cd ~/rpmbuild/SPECS
sudo dnf builddep kernel.spec

# Give a name to our kernel so that we can easily distinguish it from
# other kernels on the system. We give our kernel a name which contains
# the string foobar
cd ~/rpmbuild/SPECS
sed -i 's/# define buildid .local/%define buildid .foobar/g' kernel.spec

# Verify that sed succeeded
cd ~/rpmbuild/SPECS
grep foobar kernel.spec
# Expected result: "%define buildid .foobar"

# Build the kernel. It would have been enough to say
#   rpmbuild -bb --target=x86_64 kernel.spec
# but kernel building takes time and it is nice to know how much time.
# On my system, a kernel build takes 107 minutes.
# Furthermore, "2>&1 | tee ~/rpm-out" ensures that we both see progress
# in the command window and also have access to the output afterwards
cd ~/rpmbuild/SPECS
time rpmbuild -bb --target=x86_64 kernel.spec 2>&1 | tee ~/mydevel/rpm-out

# Install the foobar kernel
cd ~/rpmbuild/RPMS/x86_64
sudo dnf install --nogpgcheck \
  kernel-core-5.19.4-200.foobar.fc36.x86_64.rpm \
  kernel-modules-5.19.4-200.foobar.fc36.x86_64.rpm \
  kernel-5.19.4-200.foobar.fc36.x86_64.rpm

# Reboot into kernel-5.19.4-200.foobar.fc36
sudo reboot
# Check that things work as they used to do
# Reboot back to your current kernel
sudo reboot

# Remove the foobar kernel.
sudo dnf remove kernel-core-5.19.4-200.foobar.fc36.x86_64

# Summary.
# We have built and run a kernel and have done some clean-up afterwards
# We still have the source rpm in ~/mydevel/src/



# PART 2 MAKING A CHANGE TO A KERNEL

# Nuke ~/rpmbuild.
# There is a make target "make mrproper" which is "make clean" on steroids.
# But even mrproper is not enough for us in PART 2.
rm -r -f ~/rpmbuild

# Make a fresh ~/rpmbuild (again)
rpmdev-setuptree

# Unpack the kernel source rpm into ~/rpmbuild (again)
cd ~/mydevel/src
rpm -Uvh kernel-5.19.4-200.fc36.src.rpm

# Give a name to our kernel (again)
cd ~/rpmbuild/SPECS
sed -i 's/# define buildid .local/%define buildid .foobar/g' kernel.spec

# Take a look at ~/rpmbuild
ls ~/rpmbuild
# Expected result "BUILD RPMS SOURCES SPECS SRPMS"

# Note that BUILD, RPMS and SRPMS are all empty
ls ~/rpmbuild/BUILD/ ~/rpmbuild/RPMS/ ~/rpmbuild/SRPMS/

# Note that SPECS only contains the file named "kernel.spec"
ls ~/rpmbuild/SPECS

# Note that SOURCES contains a lot
ls ~/rpmbuild/SOURCES

# Note that SOURCES contains one file with extension .xz:
ls ~/rpmbuild/SOURCES/*.xz
# That is the file where the real sources are hidden

# Note that SOURCES contains some files with extension .patch:
ls ~/rpmbuild/SOURCES/*.patch

# Note that ~/rpmbuild/SOURCES/linux-kernel-test.patch is empty:
ls -l ~/rpmbuild/SOURCES/linux-kernel-test.patch

# Execute the %prep stage of kernel.spec to unpack sources and apply patches
# This just takes a few minutes
cd ~/rpmbuild/SPECS
rpmbuild -bp kernel.spec

# Note that ~/rpmbuild/BUILD now contains a single directory
ls ~/rpmbuild/BUILD

# Note that that directory contains a single directory
ls ~/rpmbuild/BUILD/*

# Note that the latter directory contains a lot
ls ~/rpmbuild/BUILD/*/*
# Those are the sources we are going to modify

# Check that you have plenty of room on your disk
df

# Make a ~/mydevel/orig directory and fill it with a copy of the sources
cp -r ~/rpmbuild/BUILD/*/* ~/mydevel/orig

# Check that you still have plenty of room on your disk
df

# Make one more copy of the sources
cp -r ~/mydevel/orig ~/mydevel/new

# Make some change inside the ~/mydevel/new tree.
# As an example, in my case, I was interested in knowing whether or not
#	if (mode_rate > max_rate)
#		return MODE_CLOCK_HIGH;
# in ~/mydevel/new/drivers/gpu/drm/nouveau/nouveau_dp.c was executed.
# So I changed those lines to something like:
#       printk(KERN_DEBUG "Hello world. mode_rate > max_rate is %s\n",
#         mode_rate > max_rate ? "true" : "false");
#	if (mode_rate > max_rate)
#		return MODE_CLOCK_HIGH;

# Check that your changes compile. Do so in yet another copy. In my case:
cp -r ~/mydevel/new ~/mydevel/build
cd ~/mydevel/build
make xconfig
# Hit Ctrl-S Ctrl-Q to save the default configuration and quit "make xconfig"
make drivers/gpu/drm/nouveau/nouveau_dp.o
# Check for errors
# Clean up
cd ~/mydevel
rm -r -f build

# If you are not satisfied with the result, make new changes and
# repeat the block above

# Generate a diff and overwrite the empty patch file from before
cd ~/mydevel
diff -uNrp orig new > ~/rpmbuild/SOURCES/linux-kernel-test.patch

# Verify that ~/rpmbuild/SOURCES/linux-kernel-test.patch is small
wc -l ~/rpmbuild/SOURCES/linux-kernel-test.patch
# My patch file contained 12 lines which is reasonable

# Build the kernel
cd ~/rpmbuild/SPECS
time rpmbuild -bb --target=x86_64 kernel.spec 2>&1 | tee ~/mydevel/rpm-out
# The build took 35 minutes on my computer as opposed to 107 minutes for
# the first build, probably because of ccache

# Install the foobar kernel
cd ~/rpmbuild/RPMS/x86_64
sudo dnf install --nogpgcheck \
  kernel-core-5.19.4-200.foobar.fc36.x86_64.rpm \
  kernel-modules-5.19.4-200.foobar.fc36.x86_64.rpm \
  kernel-5.19.4-200.foobar.fc36.x86_64.rpm

# Reboot into kernel-5.19.4-200.foobar.fc36
sudo reboot
# Open a terminal window
dmesg | grep "Hello world"
# See if the printk was executed
# Reboot back to your current kernel
sudo reboot

# Clean up.
sudo dnf remove kernel-core-5.19.4-200.foobar.fc36.x86_64
rm -r -f ~/mydevel
rm -r -f ~/rpmbuild

# Have fun