Build Your Own Kernel Modules

Created by AkemiYagi. Currently maintained by AlanBartlett and AkemiYagi.

Please note that if you make changes to your kernel, you may no longer get support or help from the CentOS development team. The procedures described here are not officially endorsed by CentOS. This tutorial is provided in a hope that it will be useful when building your own kernel modules.

There are occasions when you need to modify a kernel module or create a new one. This may be to add certain features or simply to apply a patch. In this tutorial, we will attempt to apply a bug fix to an existing module and then install it.

It is assumed that you already have the full kernel source tree installed. If you followed Section 2 of I need the Kernel Source, it will be found in the directory ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.`uname -m`/. Throughout this tutorial a system with 32-bit architecture is assumed. Hence the string returned by `uname -m` is i686 and the directory referenced above will be ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/.

<!> You are strongly advised against performing module building as root. (See: Building Source RPM as non-root under CentOS)

1. Building a kernel module (*.ko)

Let us assume, as an example, you are applying a patch to the cifs module which is located in the ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs/ directory.

1. Apply the patch(es) to the source file(s) as required.

2. Change to the root directory of the kernel source and, if this is the first time the kernel has been compiled, configure the kernel remembering to ensure that the relevant component of the kernel is set, within its configuration file (.conf), to be built as a module.

[user@host]$ cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686
[user@host linux-2.6.18.i686]$ make oldconfig
[user@host linux-2.6.18.i686]$ make menuconfig
[user@host linux-2.6.18.i686]$ make prepare

3. Create the files required for compiling external modules.

[user@host linux-2.6.18.i686]$ make modules_prepare

Note: make modules_prepare will not build a Module.symvers file even if the parameter CONFIG_MODULEVERSIONING has been set. Therefore a full kernel build will have to have been executed previously to make module versioning work. For more details, see Sections #2.4 & #7 of /usr/share/doc/kernel-doc-2.6.18/Documentation/kbuild/modules.txt

4. Compile the module by specifying the relative path to the module’s Makefile and source code.

[user@host linux-2.6.18.i686]$ make M=fs/cifs

Note: The whole directory containing the module can be located anywhere. If, for example, it is in ~/mycifs/ run the following command making sure that you are still in the root directory of the kernel source.

[user@host linux-2.6.18.i686]$ make M=~/mycifs

5. Unless you have compiled this module for debugging purposes, it should now be stripped of unnecessary symbols.

[user@host linux-2.6.18.i686]$ strip --strip-debug fs/cifs/cifs.ko

6. In this example, the file cifs.ko has just been created. As root, copy the .ko file to the /lib/modules/<kernel-version>/extra/ directory.

[root@host linux-2.6.18.i686]# cp fs/cifs/cifs.ko /lib/modules/`uname -r`/extra

For further details of the directories into which modules are installed, see Section #6 of /usr/share/doc/kernel-doc-2.6.18/Documentation/kbuild/modules.txt

7. As root, run the depmod command to update the module dependency files.

[root@host linux-2.6.18.i686]# depmod -a

8. A further few steps are required to build the different modules for the different types of kernel. Edit the main Makefile in the root directory of the kernel source. For CentOS 5, find:

EXTRAVERSION = -prep

and replace it with (as appropriate):

EXTRAVERSION = -371.11.1.el5
EXTRAVERSION = -371.11.1.el5PAE
EXTRAVERSION = -371.11.1.el5xen
EXTRAVERSION = -371.11.1.el5.centos.plus
EXTRAVERSION = -371.11.1.el5.centos.plusPAE
EXTRAVERSION = -371.11.1.el5.centos.plusxen

The number is not very important if you are going to use modprobe to install the module. (A -371.el5 module will install into a -371.11.1.el5 kernel, etc.). However the PAE and xen modifiers are required to be used for those types of kernels.

Hint: After compiling the module for the standard kernel, edit the EXTRAVERSION line to add PAE or xen and run make modules_prepare. Then run make M=xxx to compile the module for the kernel version specified on the EXTRAVERSION line.

2. Building a kernel module using Dynamic Kernel Module Support (DKMS)

The method described above builds a module for a particular kernel version. If you upgrade the kernel or change the hardware architecture, you will have to manually build the module once again. The dynamic kernel module support (DKMS) framework is basically a duplicate tree, outside of the kernel source, that holds the source and compiled binaries for a particular module. DKMS can be called on to build, install or uninstall modules. DKMS requires the module source code to be located on your system. The DKMS binary takes care of building and installing the module(s) into any kernel(s) you may have on your system.

Here we will use the same example as above and build & install the cifs module. You will need to assume root's powers for this entire section.

1. Install the kernel-devel package that matches your current kernel.

2. Install the dkms package from the Repositories/RPMForge repository.

3. Create a directory /usr/src/<module>-<module-version>/

[root@host]# mkdir /usr/src/cifs-1.45fixed/

4. Copy the module's source code to that directory.

[root@host]# cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18-i686/fs/cifs
[root@host cifs]# cp -a * /usr/src/cifs-1.45fixed/

5. Create a dkms.conf file in the directory /usr/src/<module>-<module-version>/

[root@host cifs]# cd /usr/src/cifs-1.45fixed
[root@host cifs-1.45fixed]# vi dkms.conf

The dkms.conf file will need to contain these lines:

PACKAGE_NAME="cifs"
PACKAGE_VERSION="1.45fixed"
BUILT_MODULE_NAME[0]="cifs"
DEST_MODULE_LOCATION[0]="/kernel/fs/cifs/"
AUTOINSTALL="yes"

Note: The DEST_MODULE_LOCATION[0] line will be ignored during module installation because this will always be to the /lib/modules/<kernel-version>/extra/ directory. This parameter, however, will be used when a module is uninstalled as the location to which the stored, old module (if any) will be restored.

6. Add the <module>/<module-version> to the DKMS tree.

[root@host cifs-1.45fixed]# dkms add -m cifs -v 1.45fixed

7. Compile the module, under DKMS control.

[root@host cifs-1.45fixed]# dkms build -m cifs -v 1.45fixed

8. Install the module, again under DKMS control.

[root@host cifs-1.45fixed]# dkms install -m cifs -v 1.45fixed

Other DKMS actions to note are uninstall, remove, status and mkrpm which uninstalls the module from the kernel, removes the <module>/<module-version> from the DKMS tree, displays the current DKMS status of the system and creates a rpm file in the directory /var/lib/dkms/<module>/<module-version>/rpm/ respectively.

See also:

3. Building a kernel module rpm package (kmod)

You can also create rpm files for kernel modules. The kernel module rpm package (kmod) may then be installed by using the rpm command just like any other package. However, rpm packaging of kernel modules is handled differently from the standard packaging process. The example, below, provides the basic kernel module rpm packaging technique.

Once again we will use the cifs module as an example and create a set of kmod-cifs rpm files.

1. Install all the kernel-devel packages for the kernel version that this package will be built against.

2. The patched source code is in the ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs/ directory as in Section 1, above. Make a working copy of that directory.

[user@host]$ mkdir ~/cifs-1.45/
[user@host]$ cd ~/rpmbuild/BUILD/kernel-2.6.18/linux-2.6.18.i686/fs/cifs
[user@host]$ cp -a * ~/cifs-1.45/

3. Create a bzip2 compressed tarball containing the source directory.

[user@host]$ cd
[user@host]$ tar -jcf cifs-1.45.tar.bz2 cifs-1.45/

4. Copy the compressed tarball to the SOURCES directory.

[user@host]$ cp cifs-1.45.tar.bz2 ~/rpmbuild/SOURCES/

5. Make a copy the script kmodtool (part of the redhat-rpm-config package) in the SOURCES directory.

[user@host]$ cd ~/rpmbuild/SOURCES/
[user@host SOURCES]$ cp /usr/lib/rpm/redhat/kmodtool kmodtool-cifs-el5.sh

6. Edit your copy of the kmodtool-cifs-el5.sh file to ensure that the line referencing kmod-common is commented out and to add a line that expands to become a reference to a kmod-cifs.conf file under the %files section.

[user@host SOURCES]$ vi kmodtool-cifs-el5.sh

Line 105 onwards --

#
# RHEL5 - Remove common package requirement on general kmod packages.
# Requires: ${kmod_name}-kmod-common >= %{?epoch:%{epoch}:}%{version}
#

Line 168 onwards --

echo "%files         -n kmod-${kmod_name}${dashvariant}"
if [ "" == "$kmp_override_filelist" ];
then
    echo "%defattr(644,root,root,755)"
    echo "/lib/modules/${verrel}${variant}/"
    echo "%config /etc/depmod.d/kmod-${kmod_name}.conf"
    #BZ252188 - I've commented this out for the moment since RHEL5 doesn't
    #           really support external firmware e.g. at install time. If
    #           you really want it, use an override filelist solution.
    #echo "/lib/firmware/"
else
    cat "$kmp_override_filelist"
fi

7. Create a cifs-kmod.spec file in the SPECS directory.

[user@host SOURCES]$ cd ~/rpmbuild/SPECS/
[user@host SPECS]$ vi cifs-kmod.spec

# Define the kmod package name here.
%define kmod_name cifs

# If kversion isn't defined on the rpmbuild line, define it here.
%{!?kversion: %define kversion 2.6.18-8.el5}

Name:    %{kmod_name}-kmod
Version: 1.45
Release: 1%{?dist}
Group:   System Environment/Kernel
License: GPLv2
Summary: %{kmod_name} kernel module(s)
URL:     http://www.centos.org/

BuildRequires: redhat-rpm-config
BuildRoot:     %{_tmppath}/%{name}-%{version}-%{release}-build-%(%{__id_u} -n)
ExclusiveArch: i686 x86_64

# Sources.
Source0:  %{kmod_name}-%{version}.tar.bz2
Source10: kmodtool-%{kmod_name}-el5.sh

# Define the variants for each architecture.
%define basevar ""
%ifarch i686
%define paevar PAE
%endif
%ifarch i686 x86_64
%define xenvar xen
%endif

# If kvariants isn't defined on the rpmbuild line, build all variants for this architecture.
%{!?kvariants: %define kvariants %{?basevar} %{?xenvar} %{?paevar}}

# Magic hidden here.
%{expand:%(sh %{SOURCE10} rpmtemplate_kmp %{kmod_name} %{kversion} %{kvariants})}

# Disable the building of the debug package(s).
%define debug_package %{nil}

# Define the filter.
%define __find_requires sh %{_builddir}/%{buildsubdir}/filter-requires.sh

%description
This package provides the CentOS-5 bug-fixed %{kmod_name} kernel module (bug #1776).
It is built to depend upon the specific ABI provided by a range of releases
of the same variant of the Linux kernel and not on any one specific build.

%prep
%setup -q -c -T -a 0
for kvariant in %{kvariants} ; do
    %{__cp} -a %{kmod_name}-%{version} _kmod_build_$kvariant
done
echo "/usr/lib/rpm/redhat/find-requires | %{__sed} -e '/^ksym.*/d'" > filter-requires.sh
echo "override %{kmod_name} * weak-updates/%{kmod_name}" > kmod-%{kmod_name}.conf

%build
for kvariant in %{kvariants} ; do
    KSRC=%{_usrsrc}/kernels/%{kversion}${kvariant:+-$kvariant}-%{_target_cpu}
    %{__make} -C "${KSRC}" %{?_smp_mflags} modules M=$PWD/_kmod_build_$kvariant
done

%install
%{__rm} -rf %{buildroot}
export INSTALL_MOD_PATH=%{buildroot}
export INSTALL_MOD_DIR=extra/%{kmod_name}
for kvariant in %{kvariants} ; do
    KSRC=%{_usrsrc}/kernels/%{kversion}${kvariant:+-$kvariant}-%{_target_cpu}
    %{__make} -C "${KSRC}" modules_install M=$PWD/_kmod_build_$kvariant
done
%{__install} -d %{buildroot}%{_sysconfdir}/depmod.d/
%{__install} kmod-%{kmod_name}.conf %{buildroot}%{_sysconfdir}/depmod.d/
# Set the module(s) to be executable, so that they will be stripped when packaged.
find %{buildroot} -type f -name \*.ko -exec %{__chmod} u+x \{\} \;

%clean
%{__rm} -rf %{buildroot}

%changelog
* Wed Jan 05 2011 Alan Bartlett <ajb@elrepo.org> - 1.45
- Revised this specification file.

* Fri May 18 2007 Akemi Yagi <toracat@centos.org> - 1.45
- Initial el5 build of the kmod package.

8. Build the package.

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` cifs-kmod.spec 2> build-err.log | tee build-out.log

If you do not wish to build the kmod packages for your currently running kernel, you can specify the the kernel version on the command line. For example:

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kversion 2.6.18-371.el5' cifs-kmod.spec 2> build-err.log | tee build-out.log

will build the kmod packages for the 2.6.18-371.el5 kernel.

In a similar fashion, you may select which kernel-variant kmod package(s) to build. For example:

[user@host SPECS]$ rpmbuild -bb --target=`uname -m` --define 'kvariants ""' cifs-kmod.spec 2> build-err.log | tee build-out.log

will build just the base-kernel kmod package.

When the build completes, there will be a set of kmod-cifs rpm files in the ~/rpmbuild/RPMS/`uname -m`/ directory.

See also:

HowTos/BuildingKernelModules (last edited 2014-07-25 14:54:20 by AlanBartlett)