Using SLES and the SLE SDK to Build a Kernel Module Package (KMP)

Share
Share

Introduction

As discussed in the previous “SUSE SolidDriver Program” blog, using external kernel modules (kernel modules that were built separately from the kernel) can introduce some reliability and updatability problems.  Providing external modules via specially-formatted kernel module packages (KMPs) can reduce these problems.  This article will explain how to use SLES 12 SP1 to build a KMP that will install and work with official SLE 12 SP1 kernels.

Scope

The following sections explain how to use a local SLES server to build a KMP.  For information about using the online Open Build Service (OBS) to build KMPs, please see the article “Using OBS to Create KMPs”.

SUSE also provides in-house KMP build services for partners who have appropriate SolidDriver contracts.  For information about SolidDriver contract levels and services, see the “SUSE SolidDriver Program” blog.

This document does not provide information about developing or (back)porting driver source code.

What is a Kernel Module Package?

In a generic sense, a kernel module package is any rpm that contains kernel modules.  But for SUSE, the term KMP is used to denote an rpm that is built in compliance with SUSE’s Kernel Module Packaging Manuals.  The key feature of a SUSE kernel module package is that it installs its kernel modules such that they are not dependent on a single kernel version but instead will continue to work through kabi-compatible kernel updates.

Building a Kernel Module Package

The process to build a KMP comprises three basic steps: setting up the build system, preparing the module source code and adding packaging files, and doing the actual build.  The following sections describe these steps as well as how to test the binary KMP that was created by the build.

Step 1: Set Up the Build System

Prerequisites:

  1. Local server installed with SLES 12 SP1 and configured with SLES 12 SP1 as an enabled and accessible installation source
  2. SLES 12 SP1 DVD1 iso image (may be downloaded from http://download.suse.com )
  3. SLES 12 SP1 SDK DVD1 iso image (may be downloaded from http://download.suse.com )

Process:

  1. Add the SLES 12 SP1 SDK iso image as an installation source (SCC-registered systems can just add the Software Development Kit extension instead of adding the iso).
  2. Install the SLES 12 SP1 “C/C++ Compiler and Tools” pattern and the SLES 12 SP1 SDK “build” package:

    # zypper in -t pattern Basis-Devel
    # zypper in build
  3. Place the SLES 12 SP1 and SLES 12 SP1 SDK iso images in a work directory (e.g., /work/isos/) on the system.
  4. Create mount directories and mount the SLES 12 SP1 and SLE 12 SP1 SDK iso images so that their packages can be accessed during the build. Example:

    # mkdir -p /work/mounts/sles12sp1server
    # mkdir -p /work/mounts/sle12sp1sdk
    # mount -o loop /work/isos/SLE-12-SP1-Server-DVD-x86_64-GM-DVD1.iso /work/mounts/sles12sp1server
    # mount -o loop /work/isos/SLE-12-SP1-SDK-DVD-x86_64-GM-DVD1.iso /work/mounts/sle12sp1sdk

    Note1: Add appropriate entries in /etc/fstab to ensure that these mounts persist through reboots.
    Note2: Instead of using mounted isos, the build process can use the system’s SLES 12 SP1 and SLES 12 SP1 SDK installation sources. See “man build” for more information. This article describes using mounted isos in order to cover a wider range of configurations.

Step 2: Prepare the Kernel Module Source Code

Before packaging, it’s important to ensure that the module source code works… i.e., that the code compiles and links, and that it is configured correctly for creating external modules as described in /usr/src/linux/Documentation/kbuild/modules.txt. To quickly check that the code is ready to be packaged into a KMP, place the code in a directory, cd into that directory, then issue the following command:

# make -C /lib/modules/`uname -r`/build M=`pwd` modules

If the above make command creates the desired kernel module(s) under the current directory, then the code is ready for packaging. Otherwise, the code and/or build structure will need to be modified.

The following packaging instructions make use of the sample code in Appendix A of this document. This sample code has already been verified to build on SLES 12 SP1 and it is configured in accordance with /usr/src/linux/Documentation/kbuild/modules.txt. The code builds a module which creates an entry in the /proc filesystem.

To build a KMP from the code in Appendix A:

  1. Create a work directory structure to contain source code and packaging files. Example:

    # mkdir -p /work/sampledriver/sampledriver-1.0
  2. In the /work/sampledriver/sampledriver-1.0 directory, create a “sampledriver.c” file and a “Kbuild” file with contents as shown in Appendix A.
  3. As described in the notes at the beginning of this section, check the code and build structure:

    # cd /work/sampledriver/sampledriver-1.0
    # ls
    Kbuild sampledriver.c
    # make -C /lib/modules/`uname -r`/build M=`pwd` modules
  4. If the module builds successfully (as indicated by the creation of a “sampledriver.ko” file), then clean up the build environment:

    # make -C /lib/modules/`uname -r`/build M=`pwd` clean
  5. Create a tarball of the source code:

    # cd /work/sampledriver
    # tar cvjf sampledriver-1.0.tar.bz2 sampledriver-1.0/
  6. Remove the uncompressed sampledriver-1.0 directory:

    # rm -rf sampledriver-1.0

Step 3: Add Packaging Files

The sampledriver.c and Kbuild files are used to build the sampledriver.ko module, but an additional packaging file — an rpm spec file with KMP-specific directives — is required to build a KMP. The Kernel Module Packages Manuals (KMPMs) provide standard KMP spec file templates which make use of macros to simplify the KMP build process. The sample spec file included in Appendix A is based on the spec file template in the Code 11 KMPM.

To add the sampledriver spec file, cd into the /work/sampledriver directory and create a “sampledriver.spec” file with contents as shown in Appendix A. The /work/sampledriver/ directory should now contain 2 files:

# ls /work/sampledriver/
sampledriver-1.0.tar.bz2 sampledriver.spec

Step 4: Build the KMP

Once source code and a spec file are in place, building a KMP is simply a matter of issuing the “build” command (/usr/bin/build) with the spec file as an argument and including an option to specify the location of packages needed for the build. The “build” command creates and uses a sequestered (chroot-ed) environment, so it can build and test-install rpms (KMPs) without affecting the host system configuration.

To build the sampledriver KMP, issue the build command with the ‘–rpm’ option pointing to the SLES 12 SP1 and SLES 12 SP1 SDK package repositories that were mounted in Step 1 above:

# cd /work/sampledriver
# build --rpms /work/mounts/sles12sp1server:/work/mounts/sle12sp1sdk sampledriver.spec

The console output from the build command will show stages such as preparation, building, and test-installing the KMP. Once the command completes (and assuming a successful build), the binary and source KMP(s) will be located in appropriately-named directories under /var/tmp/build-root/home/abuild/rpmbuild/.

Step 5: Test the KMP

As mentioned above, the “build” command performs a test-install to verify package integrity. But to verify KMP format details and actual module functionality, further testing is required. The following steps test the sampledriver KMP on the build system, but because KMPs are compatible across kernel versions, this same KMP can be tested on any system that is running an official SLES 12 SP1 kernel.

To test the KMP on the build system:

  1. Install the KMP:

    # cp /var/tmp/build-root/home/abuild/rpmbuild/RPMS/x86_64/sampledriver* /tmp/
    # cd /tmp
    # rpm -ihv sampledriver-kmp-default*.rpm
  2. Load the sampledriver.ko kernel module:

    # modprobe sampledriver
  3. Run the dmesg command to view the sampledriver load message:

    # dmesg
    ...
    sampledriver: sampledriver proc entry created
  4. View the proc filesystem entry created by the module:

    # cat /proc/driver/sampledriver
    Hello from sampledriver.
  5. Unload the sampledriver.ko kernel module:

    # rmmod sampledriver
  6. Run the dmesg command to view the sampledriver unload message. Verify that the sampledriver /proc entry is removed:

    # dmesg
    ...
    sampledriver: sampledriver proc entry removed
    # cat /proc/driver/sampledriver
    cat: /proc/driver/sampledriver: No such file or directory
  7. Unload the KMP:

    # rpm -e sampledriver-kmp-default

Additional/Optional Build Steps

The steps above describe how to build and test a basic unsigned KMP. But production-level packages (including KMPs) should be signed with a key that reflects the owner and/or builder of the package. Contact your organization’s security team for information about keys and package signing.

SUSE partners who have SolidDriver Level 1 or Level 2 contracts can also “tag” their modules with a flag to indicate supportability. See the SUSE SolidDriver Program blog for more information about the supportability tag as related to SolidDriver KMPs.

The %kernel_module_package build macro (called in a KMP spec file) has a number of options that can be used to change how the resulting KMP behaves. See the SUSE Kernel Module Packages Manuals for more detailed information about these options.

Summary

This article has described one way –- using a local SLES 12 SP1 server along with the SLE 12 SP1 SDK –- to create a simple KMP. There are also online services (OBS) and programs (SUSE SolidDriver Program) that partners can use to create and/or request KMPs. But regardless of which build method a partner decides to use, providing external modules via KMPs is the best way to ensure that those modules will integrate well with SUSE Linux Enterprise products.

Appendix A – Sample Files

The following files are used to build the sampledriver.ko module and sampledriver KMP discussed above. The sampledriver.spec file is based on the spec file template in the Kernel Module Packages Manual for Code 11.

sampledriver.c

/*
 * sampledriver.c - A sample driver which creates an entry
 * in the proc filesystem.
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License as published by the Free Software Foundation.
 *
 * A copy of the GNU General Public License can be obtained
 * from http://www.gnu.org/.
 */

#include <linux/module.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static int sampledriver_proc_show(struct seq_file *s, void *v)
{
    seq_printf(s, "Hello from sampledriver.\n");
    return 0;
}

static int sampledriver_proc_open(struct inode *inode, \
                                  struct file *file)
{
    return single_open(file, sampledriver_proc_show, NULL);
}

static const struct file_operations sampledriver_proc_fops = {
    .owner          = THIS_MODULE,
    .open           = sampledriver_proc_open,
    .read           = seq_read,
    .llseek         = seq_lseek,
    .release        = single_release,
};

static int __init init_sampledriver(void)
{
    if (proc_create("driver/sampledriver", 0, \
                    NULL, &sampledriver_proc_fops) == NULL) {
        printk(KERN_INFO "sampledriver:  cannot create proc entry\n");
        return 1;
    }
    printk(KERN_INFO "sampledriver:  proc entry created\n");
    return 0;
}

static void __exit exit_sampledriver(void)
{
    remove_proc_entry("driver/sampledriver", NULL);
    printk(KERN_INFO "sampledriver:  proc entry removed\n");
}

MODULE_DESCRIPTION("Sample driver");
MODULE_LICENSE("GPL");
module_init(init_sampledriver);
module_exit(exit_sampledriver);

Kbuild

obj-m += sampledriver.o

sampledriver.spec

#norootforbuild

Name: sampledriver
BuildRequires: %kernel_module_package_buildreqs
License: GPL
Group: System/Kernel
Summary: Sample Driver
Version: 1.0
Release: 0
Source0: %name-%version.tar.bz2
BuildRoot: %{_tmppath}/%{name}-%{version}-build

%kernel_module_package

%description
This package contains the sampledriver.ko module.

%prep
%setup
set -- *
mkdir source
mv "$@" source/
mkdir obj

%build
for flavor in %flavors_to_build; do
    rm -rf obj/$flavor
    cp -r source obj/$flavor
    make -C %{kernel_source $flavor} modules M=$PWD/obj/$flavor
done

%install
export INSTALL_MOD_PATH=$RPM_BUILD_ROOT
export INSTALL_MOD_DIR=updates
for flavor in %flavors_to_build; do
    make -C %{kernel_source $flavor} modules_install M=$PWD/obj/$flavor
done

%changelog
* Mon Jul 18 2016 – andavis@suse.com
- Initial version.
Share
(Visited 36 times, 1 visits today)

Comments

  • Hello, this is a great article! nice! However it might be interesting to make a TID out of it, I fear this blog post will rest unnoticed : /

  • Leave a Reply

    Your email address will not be published. Required fields are marked *

    Avatar photo
    16,926 views