What is this?

Stock versions of the Linux kernel will boot, but often times it does not have support (a driver) for hardware devices that we use at SLAC.  It's also possible that the driver exists in the Linux kernel but we need a different version of it.

This document describes how to add a kernel module to buildroot, which is what we use to build our Linux RT kernels.

Links

Terminology

  • buildroot (tool) - the thing we download from buildroot.org that we use to build the things necessary to boot a computer
  • buildroot (artifacts) - the things that are built by buildroot (tool) and are needed to boot a computer
  • buildroot customizations - the things we put in the "site" directory of our buildroot (tool), that are then built into buildroot (artifacts)

What not to do

What we don't want to do is put the source code for a kernel module outside our buildroot customizations.  While it is technically possible to build the kernel module outside buildroot, the next time we have a new buildroot (artifacts) the kernel module that you build may not work with the kernel, and definitely won't work if we change the version identifier of the kernel.  Because we need to differentiate our kernel versions, we really want to change the version identifier of the kernel.

We also don't want to have source code contributing to our buildroot (artifacts) saved in various locations.

What to do

Buildroot (tool) provides a mechanism for integrating just about every kind of project you could possibly think of.  One of them is a kernel module.  Another one of them is a kernel module with user space library (.a / .so) support, as is very typical.

If we use the buildroot-defined mechanism for including projects, then the kernel module will always work (modulo bugs, etc.) with the built kernel, and the source code for the kernel and user space libraries will stay with buildroot (tool).

Documentation

Buildroot has great documentation.  Mostly follow the documentation here, but also the generic documentation for adding packages starting here.  Be aware that the documentation for your specific version of buildroot may vary slightly, so make sure you compare with the documentation that you download when you download and unzip buildroot, which you can find at docs/manual/manual.text.

What I write about in the remainder of this document is distilled from buildroot's documentation, and having gone through this exercise once with an Intel driver.

Breaking out the parts

  • hook into "make menuconfig"
  • create a makefile that defines how to perform the functions needed to construct your kernel module
  • select your config option
  • make

Your kernel module is a customization, so it must be in the "site" directory.  This is what gets checked into git, and applied (overlayed) to buildroot.

Create a directory in site/br2-external/packages/.  I will call my directory intel-i40e as that's the driver I needed to upgrade.  In that directory, you must create a makefile with the same name as the directory.  So, I have intel-i40e.mk.  You also need a file named Config.in that defines your config option in "make menuconfig".  Your config option will appear under "External options" in "make menuconfig" and I'm not sure if that is customizable.  The format of Config.in is well defined in the buildroot documentation.  The coding style is also important, as your code will not build it it's the wrong style!

Inside your makefile (intel-i40e.mk) you must define how the source code is found, how it is compiled/linked, and how to install it on (in) the rootfs.  Generally, the buildroot (tool) has macros available that make it easy to define this.  For example, if your code is available for download as a .tar.gz, you simply supply the URL, as you see on lines 7-9 here.


  1 ################################################################################
  2 #
  3 # intel-i40e
  4 #
  5 #################################################################################
  6 
  7 INTEL_I40E_VERSION = 2.23.17
  8 INTEL_I40E_SOURCE = i40e-$(INTEL_I40E_VERSION).tar.gz
  9 INTEL_I40E_SITE = https://downloadmirror.intel.com/786085
 10 INTEL_I40E_INSTALL_STAGING = YES
 11 INTEL_I40E_LICENSE = GPL-2.0
 12 INTEL_I40E_LICENSE_FILES = COPYING
 13 
 14 define INTEL_I40E_BUILD_CMDS
 15     touch $(BUILD_DIR)/intel-i40e-$(INTEL_I40E_VERSION)/Makefile
 16     $(TARGET_MAKE_ENV) KSRC=$(LINUX_DIR) $(MAKE) $(TARGET_CONFIGURE_OPTS) -C $(BUILD_DIR)/intel-i40e-$(INTEL_I40E_VERSION)/src default
 17     gzip -v $(BUILD_DIR)/intel-i40e-$(INTEL_I40E_VERSION)/src/i40e.ko
 18     gzip -v $(BUILD_DIR)/intel-i40e-$(INTEL_I40E_VERSION)/src/intel_auxiliary.ko
 19 endef
 20 
 21 define INTEL_I40E_INSTALL_STAGING_CMDS
 22     $(INSTALL) -d -m 0777 $(STAGING_DIR)/lib/modules/4.14.139-rt66/kernel/drivers/ethernet
 23     $(INSTALL) -D -m 0644 $(BUILD_DIR)/intel-i40e-$(INTEL_I40E_VERSION)/src/*.ko.gz $(STAGING_DIR)/lib/modules/4.14.139-rt66/kernel/drivers/ethernet
 24 endef
 25 
 26 define INTEL_I40E_INSTALL_TARGET_CMDS
 27     $(INSTALL) -d -m 0777 $(TARGET_DIR)/lib/modules/4.14.139-rt66/kernel/drivers/ethernet
 28     $(INSTALL) -D -m 0644 $(STAGING_DIR)/lib/modules/4.14.139-rt66/kernel/drivers/ethernet/* $(TARGET_DIR)/lib/modules/4.14.139-rt66/kernel/drivers/ethernet/
 29 endef
 30 
 31 $(eval $(kernel-module))
 32 $(eval $(generic-package))

In my case the Intel driver was peculiar.  The downloaded .tar.gz file, after unpacking, wants to be built from within the src directory.  I could have moved the src code, or not unzipped anything except the src directory, or had the top-level build call another build in the src directory.  But what I did was implement a custom build command, using the <package>_BUILD_CMDS macro.  It directly calls make in the src directory.  And because we already have a custom build command, I also gzip the resulting kernel modules, just because I noticed the other kernel modules in our Linux RT were gzipped.

On line 15 I am creating an empty Makefile, just because buildroot (tool) expects a Makefile to be present in the top-level directory.  The package would not build without it.

I define where I want the resulting kernel modules to be installed in the rootfs on lines 27 and 28.  There is probably a macro that defines this, so you don't need <package>_TARGET_CMDS, but I could not find the macro.

Lines 31 and 32 call the appropriate targets - this is boilerplate from buildroot (tool).

Now if you run "make menuconfig" you should see "External options" and in there you will find your config option as you defined it in your Config.in.

Now when you run "make" your package will follow the rules that you have defined.

Note that you can put "echo" statements in the defined rules, just to follow the progress to see what passed or failed, or what values macros have.

Also note that if you're building a kernel module, you must reference the buildroot (artifact) kernel and kernel header files.  Not the host's kernel and kernel header files.  The kernel module's Makefile will probably give you a way to define where to find those things.  In my case, I needed to define the KSRC macro when calling "make" (line 16).

  • No labels