I've been using Fedora Linux for a couple years now, and this week
I wanted to write a kernel module for some reasons. Of course, I try
to write all software I possibly can in Rust, and Linux
recently has support for writing
modules, including
out of tree
modules, in Rust! Great, so it should be really easy, just copy the
rust-out-of-tree-module
Makefile
and Kbuild
, run make
, and
insmod
ourselves to success!
If it was that easy, we would have no blog post.
Because I'm on Fedora, my first idea was to follow the Fedora documentation for building the kernel as RPM packages. There is pretty decent documentation for doing this on the Fedora docs page.
If all you want to do is build RPM packages for the Linux Kernel
exactly as it is maintained by Red Hat/Fedora maintainers, you
can follow these directions. Unfortunately, we need to set a few
configurations, which I naively tried to set in kernel-local
,
as the documentation instructs:
CONFIG_RUST=y
# CONFIG_RUST_DEBUG_ASSERTIONS is not set
CONFIG_RUST_OVERFLOW_CHECKS=y
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
These configurations do not conflict with the upstream
configurations for the same architecture/distribution, so they
will work when we add them to kernel-local
with the diff1.
Unfortunately, there is currently a bug with a fix hopefully landing soon which causes a build failure, and I couldn't figure out how to disable this selftest without setting:
CONFIG_X86_DECODER_SELFTEST=n
In the kernel-local
file. Unfortunately this configuration does
conflict with the upstream configurations, and causes an error
from the script process_configs.sh
about conflicting
configurations:
Error: Mismatches found in configuration files for x86_64 x86_64-debug
Found # CONFIG_X86_DECODER_SELFTEST is not set, after generation, had CONFIG_X86_DECODER_SELFTEST n in Source tree
Error: Mismatches found in configuration files for x86_64 x86_64
Found # CONFIG_X86_DECODER_SELFTEST is not set, after generation, had CONFIG_X86_DECODER_SELFTEST n in Source tree
error: Bad exit status from /var/tmp/rpm-tmp.JNrTT1 (%prep)
This was a common error message when playing with configurations, both when building following the Fedora documentation, and when building the Fedora ARK kernel distribution.
So we bail out of trying to build RPM packages. This kernel is just going to be for my use anyway, so this is fine.
Because the RPMs were a dead end, I ended up following the process recommended by the [Fedora Docs](docs page. First, install the dependencies as shown in the Fedora Docs. Then, we can get building.
This boils down to the following. Basically, we clone the kernel source, copy our running (since we are running Fedora already -- if you aren't, the docs linked there explain how to use dist-git to get a base configuration) configuration as the kernel configuration, enable Rust, disable the broken test, add a version string, and build!
git clone git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
cd linux
cp /boot/config-`uname -r`* .config
sed -i 's/CONFIG_X86_DECODER_SELFTEST.*/CONFIG_X86_DECODER_SELFTEST=n/' .config
cat <<EOF >>.config
CONFIG_RUST=y
# CONFIG_RUST_DEBUG_ASSERTIONS is not set
CONFIG_RUST_OVERFLOW_CHECKS=y
# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
EOF
sed -i '0,/EXTRAVERSION/s/EXTRAVERSION.*/EXTRAVERSION = -custom-rust/' Makefile
make oldconfig
make bzImage
make modules
If you don't have secure boot enabled, you can just run:
sudo make modules_install
sudo make install
If you do have secure boot enabled, you're going to need to create a keypair, register it as a Machine Owner Key (MOK), and sign the kernel with it. The Red Hat docs for this process are quite good, and we can follow them directly. You may also need to follow the "Secure Boot" section in the Fedora documentation linked above.
We need to sign both modules and the custom kernel, so we'll take
care of both of those things. I think most people who are signing
one kernel or module will probably need to sign more things,
so I don't exactly follow the docs (which would have you generate
your keys and such to the linux
directory). First, we create
two key pairs:
sudo efikeygen --dbdir /etc/pki/pesign \
--self-sign \
--kernel \
--common-name 'CN=rhart-custom-kernel-key' \
--nickname 'rhart-custom-kernel-key'
sudo efikeygen --dbdir /etc/pki/pesign \
--self-sign \
--module \
--common-name 'CN=rhart-custom-module-key' \
--nickname 'rhart-custom-module-key'
After generating the pairs, I created a directory for them and
exported both public keys. You'll be prompted for a password
for eac mokutil
command, just make sure to remember that
password because you will need it after a reboot.
mkdir ~/.certs
sudo certutil -d /etc/pki/pesign \
-n 'rhart-custom-kernel-key' \
-Lr \
> ~/.certs/rhart-custom-kernel.cer
sudo certutil -d /etc/pki/pesign \
-n 'rhart-custom-module-key' \
-Lr \
> ~/.certs/rhart-custom-module.cer
sudo pk12util -o ~/.certs/rhart-custom-module.p12 \
-n 'rhart-custom-module-key' \
-d /etc/pki/pesign
sudo openssl pkcs12 -in ~/.certs/rhart-custom-module.p12 \
-out ~/.certs/rhart-custom-module.priv \
-nocerts \
-nodes
sudo mokutil --import ~/.certs/rhart-custom-kernel.cer
sudo mokutil --import ~/.certs/rhart-custom-module.cer
Then, reboot your machine:
sudo reboot now
On reboot, you'll get a big scary blue screen for MOK. Select the "Install Key" option (which may also be called "Enroll MOK"), select your key nickname, and enter the password. Do it a second time with the other key. After that, select the reboot option to reboot with your keys registered.
Now that we can sign our kernel, go ahead and run:
sudo make install
This installs the kernel to (in my case), /boot/vmlinuz-6.7.0-rc1+
.
To sign it, I just ran:
sudo pesign --certificate 'rhart-custom-kernel-key' \
--in /boot/vmlinuz-6.7.0-rc1+ \
--sign \
--out /boot/vmlinuz-6.7.0-rc1+.signed
mv /boot/vmlinuz-6.7.0-rc1+.signed /boot/vmlinuz-6.7.0-rc1+
You may not need them, but if you want to install (and sign) external modules, run:
sudo make modules_install
The modules will be signed with a default key.
On reboot, select the new kernel from the list and boot into it.
If you can boot, congrats!
Go ahead and clone the Rust out of tree repo and build the module:
git clone /https://github.com/Rust-for-Linux/rust-out-of-tree-module
make
If you have secure boot and SELinux and all that jazz off, you can just:
sudo insmod
Otherwise, you'll see:
insmod: ERROR: could not insert module rust_out_of_tree.ko: Key was rejected by service
So we sign the module:
sudo /path/to/your/repo/linux/scripts/sign-file sha256 \
~/.certs/rhart-custom-module.priv \
~/.certs/rhart-custom-module.cer \
rust_out_of_tree.ko
And now we can load it!
Enjoy the Rust kernel development :)
diff --git a/kernel-local b/kernel-local
index 8c32be5be..ad8b799e1 100644
--- a/kernel-local
+++ b/kernel-local
@@ -1,2 +1,4 @@
-# This file is intentionally left empty in the stock kernel. Its a nicety
-# added for those wanting to do custom rebuilds with altered config opts.
+CONFIG_RUST=y
+# CONFIG_RUST_DEBUG_ASSERTIONS is not set
+CONFIG_RUST_OVERFLOW_CHECKS=y
+# CONFIG_RUST_BUILD_ASSERT_ALLOW is not set
diff --git a/kernel.spec b/kernel.spec
index 9879fa747..490a380e5 100644
--- a/kernel.spec
+++ b/kernel.spec
@@ -159,7 +159,7 @@ Summary: The Linux kernel
# to build the base kernel using the debug configuration. (Specifying
# the --with-release option overrides this setting.)
%define debugbuildsenabled 1
-# define buildid .local
+%define buildid .local
%define specrpmversion 6.7.0
%define specversion 6.7.0
%define patchversion 6.7
Now that nightly is 1.76+ you may need to
rustup toolchain install stable
,rustup default stable
,rustup component add rust-src