Creating and using a custom Linux kernel on Guix System

  • 时间: 2019-05-22 12:32:16

Efraim Flashner — May 21, 2019

Guix is, at its core, a source based distribution withsubstitutes,and as such building packages from their source code is an expected partof regular package installations and upgrades. Given this startingpoint, it makes sense that efforts are made to reduce the amount of timespent compiling packages, and recent changes and upgrades to thebuilding and distribution of substitutes continues to be a topic ofdiscussion within Guix.

One of the packages which I prefer to not build myself is theLinux-Libre kernel. The kernel, while not requiring an overabundance ofRAM to build, does take a very long time on my build machine (which mychildren argue is actually their Kodi computer), and I will often delayreconfiguring my laptop while I want for a substitute to be prepared bythe official build farm. The official kernel configuration, as is thecase with many GNU/Linux distributions, errs on the side ofinclusiveness, and this is really what causes the build to take such along time when I build the package for myself.

The Linux kernel, however, can also just be described as a packageinstalled on my machine, and as such can be customized just like anyother package. The procedure is a little bit different, although thisis primarily due to the nature of how the package definition is written.

The linux-libre kernel package definition is actually a procedurewhich creates a package.

(define* (make-linux-libre version hash supported-systems                           #:key                           ;; A function that takes an arch and a variant.                           ;; See kernel-config for an example.                           (extra-version #f)                           (configuration-file #f)                           (defconfig "defconfig")                           (extra-options %default-extra-linux-options)                           (patches (list %boot-logo-patch)))  ...)

The current linux-librepackage is for the 5.1.x series, and isdeclared like this:

(define-public linux-libre  (make-linux-libre %linux-libre-version                    %linux-libre-hash                    '("x86_64-linux" "i686-linux" "armhf-linux" "aarch64-linux")                    #:patches %linux-libre-5.1-patches                    #:configuration-file kernel-config))

Any keys which are not assigned values inherit their default value fromthe make-linux-libredefinition. When comparing the two snippets above,you may notice that the code comment in the first doesn't actually referto the #:extra-versionkeyword; it is actually for #:configuration-file.Because of this, it is not actually easy to include a custom kernelconfiguration from the definition, but don't worry, there are other waysto work with what we do have.

There are two ways to create a kernel with a custom kernel configuration.The first is to provide a standard .configfile during the buildprocess by including an actual .configfile as a native input to ourcustom kernel. Thefollowing is a snippet from the custom 'configure phase of the make-linux-librepackage definition:

(let ((build  (assoc-ref %standard-phases 'build))      (config (assoc-ref (or native-inputs inputs) "kconfig")))  ;; Use a custom kernel configuration file or a default  ;; configuration file.  (if config      (begin        (copy-file config ".config")        (chmod ".config" #o666))      (invoke "make" ,defconfig))

Below is a sample kernel package for one of my computers. Linux-Libreis just like other regular packages and can be inherited and overriddenlike any other:

(define-public linux-libre/E2140  (package    (inherit linux-libre)    (native-inputs     `(("kconfig" ,(local-file "E2140.config"))      ,@(alist-delete "kconfig"                      (package-native-inputs linux-libre))))))

In the same directory as the file defining linux-libre-E2140is a filenamed E2140.config, which is an actual kernel configuration file. Ileft the defconfig keyword of make-linux-libreblank, so the onlykernel configuration in the package is the one which I included as anative-input.

The second way to create a custom kernel is to pass a new value to theextra-options keyword of the make-linux-libreprocedure. Theextra-options keyword works with another function defined right below it:

(define %default-extra-linux-options  `(;;   ("CONFIG_DEVPTS_MULTIPLE_INSTANCES" . #t)   ;; Modules required for initrd:   ("CONFIG_NET_9P" . m)   ("CONFIG_NET_9P_VIRTIO" . m)   ("CONFIG_VIRTIO_BLK" . m)   ("CONFIG_VIRTIO_NET" . m)   ("CONFIG_VIRTIO_PCI" . m)   ("CONFIG_VIRTIO_BALLOON" . m)   ("CONFIG_VIRTIO_MMIO" . m)   ("CONFIG_FUSE_FS" . m)   ("CONFIG_CIFS" . m)   ("CONFIG_9P_FS" . m)))(define (config->string options)  (string-join (map (match-lambda                      ((option . 'm)                       (string-append option "=m"))                      ((option . #t)                       (string-append option "=y"))                      ((option . #f)                       (string-append option "=n")))                    options)               "\n"))

And in the custom configure script from the make-linux-librepackage:

;; Appending works even when the option wasn't in the;; file.  The last one prevails if duplicated.(let ((port (open-file ".config" "a"))      (extra-configuration ,(config->string extra-options)))  (display extra-configuration port)  (close-port port))(invoke "make" "oldconfig"))))

So by not providing a configuration-file the .configstarts blank, andthen we write into it the collection of flags that we want. Here'sanother custom kernel which I have:

(define %macbook41-full-config  (append %macbook41-config-options          %filesystems          %efi-support          %emulation          (@@ (gnu packages linux) %default-extra-linux-options)))(define-public linux-libre-macbook41  ;; XXX: Access the internal 'make-linux-libre' procedure, which is  ;; private and unexported, and is liable to change in the future.  ((@@ (gnu packages linux) make-linux-libre) (@@ (gnu packages linux) %linux-libre-version)                      (@@ (gnu packages linux) %linux-libre-hash)                      '("x86_64-linux")                      #:extra-version "macbook41"                      #:patches (@@ (gnu packages linux) %linux-libre-5.1-patches)                      #:extra-options %macbook41-config-options))

From the above example %filesystemsis a collection of flags Icompiled enabling different filesystem support, %efi-supportenablesEFI support and %emulationenables my x86_64-linux machine to act in32-bit mode also. %default-extra-linux-optionsare the ones quotedabove, which had to be added in since I replaced them in theextra-options keyword.

This all sounds like it should be doable, but how does one even knowwhich modules are required for their system? The two places I foundmost helpful to try to answer this question were the GentooHandbook,and the documentationfrom the kernel itself. From the kernel documentation, it seems that make localmodconfigis the command we want.

In order to actually run make localmodconfigwe first need to get andunpack the kernel source code:

tar xf $(guix build linux-libre --source)

Once inside the directory containing the source code run touch .configto create an initial, empty .configto start with. make localmodconfigworks by seeing what you already have in .configandletting you know what you're missing. If the file is blank then you'remissing everything. The next step is to run:

guix environment linux-libre -- make localmodconfig

and note the output. Do note that the .configfile is still empty.The output generally contains two types of warnings. The first startwith "WARNING" and can actually be ignored in our case. The second read:

module pcspkr did not have configs CONFIG_INPUT_PCSPKR

For each of these lines, copy the CONFIG_XXXX_XXXXportion into the .configin the directory, and append =m, so in the end it lookslike this:


After copying all the configuration options, run make localmodconfigagain to make sure that you don't have any output starting with"module". After all of these machine specific modules there are acouple more left that are also needed. CONFIG_MODULESis necessary sothat you can build and load modules separately and not have everythingbuilt into the kernel. CONFIG_BLK_DEV_SDis required for reading fromhard drives. It is possible that there are other modules which youwill need.

This post does not aim to be a guide to configuring your own kernelhowever, so if you do decide to build a custom kernel you'll have toseek out other guides to create a kernel which is just right for yourneeds.

The second way to setup the kernel configuration makes more use ofGuix's features and allows you to share configuration segments betweendifferent kernels. For example, all machines using EFI to boot have anumber of EFI configuration flags that they need. It is likely that allthe kernels will share a list of filesystems to support. By usingvariables it is easier to see at a glance what features are enabled andto make sure you don't have features in one kernel but missing in another.

Left undiscussed however, is Guix's initrd and its customization. It islikely that you'll need to modify the initrd on a machine using a customkernel, since certain modules which are expected to be built may not beavailable for inclusion into the initrd.

Suggestions and contributions toward working toward a satisfactorycustom initrd and kernel are welcome!

About GNU Guix

GNU Guix is a transactional packagemanager and an advanced distribution of the GNU system that respectsuserfreedom.Guix can be used on top of any system running the kernel Linux, or itcan be used as a standalone operating system distribution for i686,x86_64, ARMv7, and AArch64 machines.

In addition to standard package management features, Guix supportstransactional upgrades and roll-backs, unprivileged package management,per-user profiles, and garbage collection. When used as a standaloneGNU/Linux distribution, Guix offers a declarative, stateless approach tooperating system configuration management. Guix is highly customizableand hackable throughGuile programming interfaces and extensions to the Schemelanguage.