Ubuntu Weekly Recipe

第791回自作のカーネルモジュールをDKMS対応にする

長い人生において誰もが一度は遭遇するであろう経験のひとつが「カーネルモジュールのビルド」です。ちょっと盛りすぎではないかと思われるかもしれませんが、それでも本連載の読者層であれば、おそらく大半の人がなにがしかのカーネルモジュールをビルドしたことがあるのではないでしょうか。今回は自作を含むサードパーティのカーネルモジュールの管理が楽になるDKMS(Dynamic Kernel Module Support)の使い方と、セキュアブートへの対応方法を紹介します。

Ubuntuのカーネルについて

Ubuntuのカーネルは、アップストリームであるLinuxカーネルの特定のバージョンに対して、Ubuntu独自のパッチを加えて構築されています。その仕組みにはUbuntu固有の手順も多く、本連載ではこれまでにもさまざまな方法でカーネルをビルド・インストールする手段を紹介してきました。代表的なものだけでも次の記事が存在します。

第278回Ubuntuカーネルとの付き合い方
Ubuntuカーネルの基本的な使い方。
第333回カーネルパッケージをビルドしよう
Ubuntuカーネルのgitリポジトリもしくはカーネルソースパッケージを利用した、Ubuntuのカーネルパッケージのビルド方法。
第524回Hades Canyon/Kaby Lake GのdGPUを有効化する
Ubuntuのメインラインビルドを利用したビルド済み最新カーネルのインストール方法。
第526回Ubuntuで最新のカーネルをお手軽にビルドする方法
バニラカーネルからDebianパッケージをビルドする方法。
第578回メインラインカーネルにパッチをあてる
Ubuntuパッチとセットでアップストリームのカーネルをデイリーでビルドしている、メインラインカーネルをカスタマイズする方法。

しかしながら、たとえば新しいハードウェアの対応等を目的として新規にカーネルモジュールを追加したいだけなら、カーネル本体を再ビルドする必要はありません。モジュールだけをビルドしてロードすれば事足ります。ただしカーネルのソースツリーの外にあるモジュール[1]を独自にビルドする場合、次のような懸念事項が存在します。

  • セキュアブートを有効化した環境では、モジュールの署名が必要
  • カーネルのバージョンが更新された場合、再ビルドが必要になることがある

前者については、未署名のモジュールをロードしようとすると次のようなエラーになります。

モジュールのロード時にエラーになる
$ sudo insmod hello.ko
insmod: ERROR: could not insert module hello.ko: Operation not permitted

カーネル側にもエラーログが残る
[22505.175003] Lockdown: insmod: unsigned module loading is restricted; see man kernel_lockdown.7

これに対応する方法は「セキュアブートをオフにする」「モジュールを独自の鍵で署名して、その鍵をUEFIのMOKリストに追加する」の2択です。たとえば第782回のMellanoxのOFEDドライバーをセキュアブートが有効化されたUbuntuにインストールするは後者を採用していました。

さらにやっかいなのが「カーネルのバージョンが更新された時」です。UbuntuのカーネルはCONFIG_MODVERSIONSを有効化しているため、ABI[2]が変わらない程度の更新であれば、同じバイナリをそのままロードできます。しかしながらABIが変わると再ビルドが必要になってくるのです。

これらの懸念事項を解決できる仕組みがDKMS(Dynamic Kernel Module Support)です。DKMSを使えば、カーネルが更新されるたびに自動的にカーネルモジュールを再ビルドし、再署名を行ってくれます。よってシステムを再起動するだけで、サードパーティのモジュールをロードできるのです。また、DKMS対応のDebianパッケージもかんたんに作れる仕組みが備わっているため、他のマシンへの配布も楽になります。

前述の第782回のOFEDドライバーだけでなく、VirtualBoxやNVIDAなどの何らかの理由でカーネルにソースコードを取り込めないもの・より新しいドライバーを使いたいものについては、その多くがDKMSを利用したソースアーカイブが提供されています。

今回は、まずシンプルなサードパーティのカーネルドライバーを作成した上で、そのドライバーをDKMSに対応させてみましょう。

ゼロからカーネルドライバーを作ろう

実はカーネルドライバーを作ること自体はそこまで難しくありません。とりあえずC言語を使える環境とカーネルのビルドシステムとヘッダーファイルがあればなんとかなります。余計な依存関係もなく、言語固有のパッケージ管理システムに悩む必要もありませんし、コード自体もシンプルなC言語で済みますので、ただただコーディングに専念すれば良いことになります[3]

最低限用意しなければいけないのは、Makefileとソースコードの2ファイルです。まずはソースコードは次のように記述します。これをhello.cとして保存してください。

// SPDX-License-Identifier: GPL-2.0-only
#include <linux/module.h>
#include <linux/init.h>

static int __init hello_init(void)
{
        pr_info("%s: Hello world!\n", __func__);
        return 0;
}

static void __exit hello_exit(void)
{
        pr_info("%s: exit hello module\n", __func__);
}

module_init(hello_init);
module_exit(hello_exit);

MODULE_DESCRIPTION("hello");
MODULE_AUTHOR("Mitsuya Shibata");
MODULE_LICENSE("GPL v2");

内容自体は定番のコードで、カーネルモジュールをロードしたらカーネルのログバッファーにHello world!を表示し、アンロードしたらexit hello moduleと表示しています。

次に「Makefile」を作成します。

# SPDX-License-Identifier: CC0-1.0

ifneq ($(KERNELRELEASE),)
obj-m  := hello.o
else
KVER ?= $(shell uname -r)
KDIR ?= /usr/lib/modules/$(KVER)/build

default:
        $(MAKE) -C $(KDIR) M=$$PWD

install:
        kmodsign sha512 \
                /var/lib/shim-signed/mok/MOK.priv \
                /var/lib/shim-signed/mok/MOK.der \
                hello.ko
        $(MAKE) -C $(KDIR) M=$$PWD modules_install
        depmod -A

clean:
        $(MAKE) -C $(KDIR) M=$$PWD clean
endif

こちらは普通のMakefileと異なる部分があります。LinuxカーネルはKbuildという仕組みを使って各種コンポーネントをビルドするようになっています。これはMakefileに特別な書式を追加することで実現しています。上記の例だとobj-mの行がそれで、カーネルモジュールとしてhello.cをコンパイルしたhello.oを使う」と指定しています。これだけでKbuildはhello.koを作成してくれるのです。

Kbuild用のMakefileとしてはobj-mの行だけでも十分なのですが、今回はLinuxカーネルとは独立してモジュールをビルドする必要があります。⁠default」⁠clean」⁠install」のターゲットを別途作っています。Kbuild用のMakefileの記述とは切り分けられるように(つまりターゲット名がLinuxカーネルのビルドシステムと衝突しないように⁠⁠、ifneq...else...endifでわけているのが、最初のポイントです。

もうひとつはKVERKDIR変数です。カーネルモジュールをビルドするためには、カーネルのヘッダーファイルとビルド用の各種スクリプトが必要になります。Ubuntuの場合は/usr/src/linux-headers-カーネルリリース名というディレクトリに最新のヘッダーファイルとビルド用の一式が最初からインストールされています[4]。よってこのディレクトリを指定する必要があるのです。

ここでは/usr/lib/modules/カーネルリリース名/buildを指定してますが、これは/usr/src/linux-headers-カーネルリリース名へのシンボリックリンクとなります。

$ ls -l /usr/lib/modules/5.15.0-1047-kvm/build
lrwxrwxrwx 1 root root 38 Nov  2 14:25 /usr/lib/modules/5.15.0-1047-kvm/build -> /usr/src/linux-headers-5.15.0-1047-kvm

さて、これをKVERのように変数にしているのにはわけがあります。Ubuntuシステムにおいて、カーネルアップデートによって新しいリリースのカーネルがインストールされた場合はどうなるでしょう。新しいカーネルでも、自作のカーネルモジュールを使いたいとします。この時、Makefile内部のディレクトリ名をuname -rなどで「実行中のカーネルリリース」のみに決め打ちしていると、新しいカーネル用のモジュールは、⁠新しいカーネルで再起動」しないと作れません。これはいろいろ面倒です。よって上記Makefileのようにカーネルのリリース名は外から変更可能とし、未指定時は初期値としてuname -rおくのが良いのです。もし特定のカーネルリリース向けにビルドしたければmake KVER=6.2.0-26-genericと実行すれば良いことになります。

話をもとに戻して、次はdefaultターゲットです。これは単にカーネルモジュールをビルドしているだけです。実際にビルドしてみましょう。まずはカーネルモジュールをビルドするためにコンパイラとmakeコマンドをインストールします。これは次のコマンドが一番かんたんです。

$ sudo apt install build-essential

あとはmakeコマンドをっこうすればビルド完了です。

$ make
make -C /usr/lib/modules/5.15.0-1047-kvm/build M=$PWD
make[1]: Entering directory '/usr/src/linux-headers-5.15.0-1047-kvm'
  CC [M]  /home/ubuntu/recipe-0791/hello.o
  MODPOST /home/ubuntu/recipe-0791/Module.symvers
  CC [M]  /home/ubuntu/recipe-0791/hello.mod.o
  LD [M]  /home/ubuntu/recipe-0791/hello.ko
  BTF [M] /home/ubuntu/recipe-0791/hello.ko
Skipping BTF generation for /home/ubuntu/recipe-0791/hello.ko due to unavailability of vmlinux
make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-1047-kvm'

この時点ではカーネルモジュールは署名されていません。セキュアブート環境でロードしようとすると冒頭のエラーメッセージのように失敗してしまいます。署名はインストール環境で実施していますがこれには先に事前準備が必要です。第782回の途中にある「MOKManagerを使ってドライバーの鍵を登録する」でも解説したように、セキュアブート向けに署名するためのMOK(Machine Owner Key)用の鍵の生成と登録が必要になるのです。まずは次に解説する手順に従って、そちらから実施しましょう。

ちなみにUbuntuインストール時に「サードパーティのドライバーを有効にする」といった項目にチェックを入れた場合、既に鍵の生成と登録は完了済みであるためこの手順はスキップできます。次のディレクトリにファイルがあるなら設定済みです。

$ ls /var/lib/shim-signed/mok/
MOK.der  MOK.priv

もしファイルが存在しない場合は次のように生成しましょう。

$ sudo update-secureboot-policy --new-key
Generating a new Secure Boot signing key:
Can't load /var/lib/shim-signed/mok/.rnd into RNG
405701BE5D7F0000:error:12000079:random number generator:RAND_load_file:Cannot open file:../crypto/rand/randfile.c:106:Filename=/var/lib/shim-signed/mok/.rnd
(中略)

生成した鍵をUEFIの変数領域にあるデータベースにインポートします。これはセキュリティの都合で、Linuxから直接はインポートできません。次のコマンドで一旦ブートローダーに鍵登録のUEFIアプリケーションMokManagerの起動を依頼して、再起動後にコンソールから登録を行います。つまりこの手順を実施するためには手元にディスプレイとキーボードもしくはIPMIやシリアルコンソールが必要になります。これらを使ってデバイスに直接アクセスできる=そのマシンの所有者である、ことを担保するわけです。逆にそれらがない状態で以下のコマンドを実行すると、Ubuntuが起動する前に止まってしまうため注意してください。

mokutilコマンドでMOKの証明書をインポートします。

$ sudo mokutil --import /var/lib/shim-signed/mok/MOK.der
[sudo] shibata のパスワード:
input password:
input password again:

sudoのパスワードを入力したあとは、MOK登録用のワンタイムパスワードを入力します。これは再起動後にMokManagerが動く際に必要になるので覚えておいてください。一回だけなのでずっと覚えておく必要はありません。シンプルな文字列でも問題ありません。 このあと再起動したら画面の指示に従ってください。手順は前述の第782回に書いていますのでそちらも参照すると良いでしょう。

MOKの準備ができたら、作成したカーネルモジュールを署名しましょう。これは前述のMakefileのinstallターゲットで実施しています。実施していることは次のとおりです。

  • kmodsignでカーネルモジュール(koファイル)を署名
  • Kbuildのmodules_installターゲットを実行
  • depmodコマンドでモジュールデータベースを更新

セキュアブート環境で独自のカーネルを使いたいのであればカーネルの署名が必要です。先ほど生成したMOKの秘密鍵と証明書を使って、kmodsignで署名を行います。セキュアブートが無効化された環境では署名は不要ですが、署名されているモジュールがロードできないわけではないため、常に署名するようにしておいても問題ないでしょう。

modules_installターゲットを実行すると、生成したカーネルモジュールを適切な場所にインストールしてくれます。Ubuntuの場合は/usr/lib/modules/カーネルリリース名/extra/以下にインストールされます。最後にdepmodコマンドでカーネルモジュールの依存関係等が書かれたファイルを更新します。ちなみにKbuildでもdepmod相当のコマンドを実行してくれるのですが、今回の作り方だと次のようにスキップした旨の警告が表示されます。

Warning: modules_install: missing 'System.map' file. Skipping depmod.

これを回避するためにMakefileの中でdepmodを明示的に実行しています。さて実際にモジュールをインストールしてみましょう。installターゲットの各コマンドは管理者権限が必要であるため、まとめてsudoで実行してしまいます。

$ sudo make install
kmodsign sha512 \
        /var/lib/shim-signed/mok/MOK.priv \
        /var/lib/shim-signed/mok/MOK.der \
        hello.ko
make -C /usr/lib/modules/5.15.0-1047-kvm/build M=$PWD modules_install
make[1]: Entering directory '/usr/src/linux-headers-5.15.0-1047-kvm'
  INSTALL /lib/modules/5.15.0-1047-kvm/extra/hello.ko
  SIGN    /lib/modules/5.15.0-1047-kvm/extra/hello.ko
  DEPMOD  /lib/modules/5.15.0-1047-kvm
Warning: modules_install: missing 'System.map' file. Skipping depmod.
make[1]: Leaving directory '/usr/src/linux-headers-5.15.0-1047-kvm'
depmod -A

これでモジュールのインストールは完了です。実際にモジュールの情報を表示してみると、きちんと署名も付加されていることがわかります。

$ modinfo hello
filename:       /lib/modules/5.15.0-1047-kvm/extra/hello.ko
license:        GPL v2
author:         Mitsuya Shibata
description:    hello
depends:
retpoline:      Y
name:           hello
vermagic:       5.15.0-1047-kvm SMP mod_unload modversions
sig_id:         PKCS#7
signer:         dkms Secure Boot Module Signature key
sig_key:        52:DA:67:99:83:7A:8C:4D:F0:F2:50:35:BF:45:48:78:4C:96:7D:4A
sig_hashalgo:   sha512
signature:      05:89:7F:F5:C8:C0:12:84:4A:F4:89:07:94:5A:26:E0:8A:67:D7:4C:
                F0:6F:6F:CF:FD:C8:2C:23:80:4A:7D:47:80:B2:02:14:E4:29:CB:C2:
                D2:A1:E9:1E:C9:70:E6:B1:33:31:4A:FB:82:B7:71:44:73:E7:C1:B4:
                EB:F8:88:2D:6B:20:75:9F:36:E2:69:ED:64:22:04:11:83:B4:15:23:
                C2:7B:96:95:91:D1:6A:DD:83:17:FE:48:3B:04:33:B5:0C:AD:07:77:
                4A:D6:DB:B5:F3:23:10:8F:59:81:6E:D9:8A:7A:E8:78:A1:17:25:68:
                C0:16:F6:55:D7:A6:86:FA:0B:A4:BD:AF:EC:FB:65:85:7E:CD:A7:11:
                F9:75:97:30:3F:46:DC:54:F4:9D:E5:98:A4:86:46:C5:69:71:D4:99:
                DB:C0:D3:1C:49:99:35:F7:74:F4:23:EC:78:59:9B:8D:06:4E:E9:81:
                4B:5E:F6:A0:6A:9D:24:DD:11:FE:BE:C8:A9:30:C1:7C:E1:F3:3E:9B:
                3B:3B:97:A8:E7:B7:06:D3:59:BE:28:9E:AE:8B:79:D7:EE:03:9F:B4:
                56:AB:11:54:BF:DB:E7:24:F4:E9:C2:7B:1D:EB:5F:60:E6:5A:9B:C6:
                56:10:DF:C1:4D:D9:C6:23:CD:BC:1B:1D:26:BE:42:7A

あとは作ったモジュールをロードするだけです。インストール前のモジュールならinsmodコマンドでロードできますし、インストールしdepmodすればmodprobeコマンドでロードできます。今回はロード時とアンロード時にメッセージを残すようにしているので、それを一度に試してみましょう。

$ sudo modprobe hello
$ sudo modprobe -r hello
$ journalctl -r -k | head -n 2
Dec 04 14:19:41 dkms kernel: hello_exit: exit hello module
Dec 04 14:19:35 dkms kernel: hello_init: Hello world!

上記のようにロード時とアンロード時のメッセージが残っていたら成功です。あとはこれを改造していくだけで、任意のカーネルドライバーを作成できます。

もしhelloモジュールが不要になったなら削除しておきましょう。

$ sudo rm /lib/modules/カーネルリリース名/extra/hello.ko
$ sudo depmod -A

これでカーネルドライバーの最低限の作り方がわかりました。

カーネルドライバーをDKMSに対応させる

ここまで作ると次のような改善ポイントが浮かび上がってきます。

  • 複数のカーネルリリースに対してビルドする際にmake KVER=XXXを何度も実行しなくてはならない
  • インストールするターゲットごとにMOKの作成が必要
  • インストールするターゲットごとにソースファイルを配布しないといけない

これをある程度解決するのが今回紹介するDKMSです。ようやくここまでたどり着きました。DKMSに対応すれば、上記を次のように「回避・解決」してくれます。

  • DKMSに対応するとカーネルが更新される度に、自動的に「インストールされているすべてのカーネル」に対してモジュールの再ビルドと署名を実行します
  • DKMSパッケージをインストールすると、MOKの生成とUEFIへのインポートを行ってくれます。ただし初回の再起動時のMokManagerの操作は必要です
  • DKMSに対応したカーネルモジュールのパッケージはソースコードを/usr/src以下に保存しますので、ユーザー側がソースコードを意識することはありません

またDKMSにはDebian/RPMパッケージを生成する仕組みも備わっていますので、パッケージ化して配布する方法もかんたんです。

さっそく先ほど作ったカーネルドライバーをDKMSに対応してみましょう。まず、DKMSのパッケージとDebianパッケージ化するためのdebhelperパッケージをインストールします。

$ sudo apt install dkms debhelper

次に/usr/srcパッケージ名-バージョンという名前のディレクトリを作り、先ほど作ったhello.cをコピーします。実際のところディレクトリはどこでも良いです。より厳密にやるなら/usr/local/srcのほうが正しいかもしれません。

$ sudo mkdir /usr/src/hello-1.0
$ sudo cp hello.c /usr/src/hello-1.0/

ただしMakefileは先ほどと異なり次のようにします。

# SPDX-License-Identifier: CC0-1.0

obj-m  := hello.o

ifneq ($(KERNELRELEASE),)
KVER ?= $(shell uname -r)
endif
KDIR ?= /usr/lib/modules/$(KVER)/build

default:
        $(MAKE) -C $(KDIR) M=$$PWD

clean:
        $(MAKE) -C $(KDIR) M=$$PWD clean

DKMSのmake実行時は自動的にKERNELREASEが設定されているために、ifneq...endifの範囲を絞りました。またinstallはDKMSがやってくれるので削除しています。ただしターゲットがぶつかる可能性やDKMSを経由せずに手動でビルドすることも考えると、本来はトップディレクトリのMakefileとカーネルモジュール用のMakefileは分けるべきなのでしょう。

最後にDKMS用の設定ファイルであるdkms.confを同じディレクトリに作成します。

PACKAGE_NAME="hello"
PACKAGE_VERSION="1.0"
CLEAN="make clean"
MAKE[0]="make KVER=$kernelver"
BUILT_MODULE_NAME[0]="hello"
DEST_MODULE_LOCATION[0]="/updates"
AUTOINSTALL="yes"

書式と変数についてはdkmsのmanページを参照してください。ここで個別に設定が必要なのはPACKAGE_NAMEPACKAGE_VERSIONMAKE[0]BUILT_MODULE_NAME[0]です。特にMAKE[0]についてはKVER変数を渡すのを忘れないようにしてください。$kernelverはDKMSが勝手に設定してくれる変数です。ちなみにMakefileをわけると、このKVERの設定も不要になります。またUbuntuの場合、DEST_MODULE_LOCATION[0]の指定は必要ではあるものの内容は無視されるようです。

これをDKMSシステムに登録しましょう。

$ sudo dkms add hello/1.0
Creating symlink /var/lib/dkms/hello/1.0/source -> /usr/src/hello-1.0
$ ls -l /var/lib/dkms/hello/1.0/source
lrwxrwxrwx 1 root root 18 Dec  3 05:18 /var/lib/dkms/hello/1.0/source -> /usr/src/hello-1.0

これは任意のディレクトリにあるソースツリーに対するシンボリックリンクを/var/lib/dkms以下に作成します。これによりDKMSはどのソースツリーをビルドしなければいけないかを把握するのです。DKMSの対象から取り外したい場合は、addコマンドの代わりにremoveコマンドを実行してください。

DKMSでカーネルモジュールを手動ビルドしてみましょう。これはdkms buildを使います。

$ sudo dkms build hello/1.0
(中略)
Building module:
cleaning build area...(bad exit status: 2)
make -j2 KERNELRELEASE=5.15.0-1047-kvm KVER=5.15.0-1047-kvm...
Signing module:
 - /var/lib/dkms/hello/1.0/5.15.0-1047-kvm/x86_64/module/hello.ko
Nothing to do.
cleaning build area...(bad exit status: 2)

再ビルドしたい場合はsudo dkms unbuild hello/1.0してからもう一度ビルドすればOKです。ちなみにより新しいDKMSだとsudo dkms build hello/1.0 --forceという方法も使えるようです

ビルドしたカーネルモジュールをシステムにインストールするにはdkms installを使います。

$ sudo dkms install hello/1.0

hello.ko:
Running module version sanity check.
 - Original module
   - No original module exists within this kernel
 - Installation
   - Installing to /lib/modules/5.15.0-1047-kvm/updates/dkms/

depmod...

ここまでできたら実際にsudo modprobe helloしてみると良いでしょう。ちなみにアンインストールはsudo dkms uninstall hello/1.0です。

カーネルアップデート時(カーネルパッケージのインストール時や削除時)の処理は、次のスクリプトが実行されることで自動的にモジュールを再ビルドしています。

/etc/kernel/header_postinst.d/dkms
/etc/kernel/install.d/dkms
/etc/kernel/postinst.d/dkms
/etc/kernel/prerm.d/dkms

最後にここまでの成果物をDebianパッケージ化してみましょう。これはdkms mkdebを実行するだけです。

$ sudo dkms mkdeb hello/1.0
Using /etc/dkms/template-dkms-mkdeb
copying template...
modifying debian/README.Debian...
modifying debian/changelog...
modifying debian/compat...
modifying debian/control...
modifying debian/copyright...
modifying debian/dirs...
modifying debian/postinst...
modifying debian/prerm...
modifying debian/rules...
copying legacy postinstall template...
Copying source tree...
Building binary package...dpkg-buildpackage: warning: using a gain-root-command while being root
 dpkg-source --before-build .
 fakeroot debian/rules clean
dh_clean: warning: Compatibility levels before 10 are deprecated (level 7 in use)
 debian/rules build
 fakeroot debian/rules binary
dh_installdirs: warning: Compatibility levels before 10 are deprecated (level 7 in use)
dh_strip: warning: Compatibility levels before 10 are deprecated (level 7 in use)
dh_compress: warning: Compatibility levels before 10 are deprecated (level 7 in use)
dh_installdeb: warning: Compatibility levels before 10 are deprecated (level 7 in use)
dh_shlibdeps: warning: Compatibility levels before 10 are deprecated (level 7 in use)
 dpkg-genbuildinfo --build=binary -O../hello-dkms_1.0_amd64.buildinfo
 dpkg-genchanges --build=binary -O../hello-dkms_1.0_amd64.changes
 dpkg-source --after-build .

Moving built files to /var/lib/dkms/hello/1.0/deb...
Cleaning up temporary files...

これで/var/lib/dkms以下に生成されたDebianパッケージが配置されます。中身を確認してみましょう。

$ dpkg-deb --info /var/lib/dkms/hello/1.0/deb/hello-dkms_1.0_amd64.deb
 new Debian package, version 2.0.
 size 5050 bytes: control archive=1306 bytes.
     292 bytes,    10 lines      control
     247 bytes,     4 lines      md5sums
    1228 bytes,    49 lines   *  postinst             #!/bin/sh
     332 bytes,    28 lines   *  prerm                #!/bin/sh
 Package: hello-dkms
 Version: 1.0
 Architecture: amd64
 Maintainer: Dynamic Kernel Modules Support Team <[email protected]>
 Installed-Size: 21
 Depends: dkms (>= 1.95)
 Provides: hello-modules (= 1.0)
 Section: misc
 Priority: optional
 Description: hello driver in DKMS format.

パッケージ名等は自動的に決定されます。Depends「dkms」があるため、このパッケージをインストールするだけで、MOKの生成までは自動的に行ってくれます。その後はインストール時の画面上の指示に従って、MokManager用のパスワードの設定や再起動を行って、生成したMOKキーの登録を行うことになります。

$ dpkg-deb --contents /var/lib/dkms/hello/1.0/deb/hello-dkms_1.0_amd64.deb
drwxr-xr-x root/root         0 2023-12-03 06:22 ./
drwxr-xr-x root/root         0 2023-12-03 06:22 ./usr/
drwxr-xr-x root/root         0 2023-12-03 06:22 ./usr/share/
drwxr-xr-x root/root         0 2023-12-03 06:22 ./usr/share/hello-dkms/
-rwxr-xr-x root/root      8137 2023-12-03 06:22 ./usr/share/hello-dkms/postinst
drwxr-xr-x root/root         0 2023-12-03 06:22 ./usr/src/
drw-r-xr-x root/root         0 2023-12-03 06:04 ./usr/src/hello-1.0/
-rw-r--r-- root/root       442 2023-12-03 06:18 ./usr/src/hello-1.0/Makefile
-rw-r--r-- root/root       175 2023-12-03 06:18 ./usr/src/hello-1.0/dkms.conf
-rw-r--r-- root/root       413 2023-12-03 06:18 ./usr/src/hello-1.0/hello.c

中身はとてもシンプルですね。./usr/share/hello-dkms/postinstはここまで実行したdkms add/built/installを自動的に実施してくれるスクリプトです。よってユーザーは次のようにパッケージをインストールするだけで、カーネルのビルドや署名・インストールを意識する必要はありません。

$ sudo apt install ./hello-dkms_1.0_amd64.deb

Linuxカーネルの中身を知る上で、ソースコードやドキュメント・書籍を読むことも重要ですが、実際に手を動かして何か作ってみることも大事です。とは言え既存のカーネルを修正してビルドするには、それなりに時間がかかります。正しい手順を踏むのも一苦労ですし、それが起動するかどうか・起動しなくなったときにどうすれば良いかを探るのは、それはそれで勉強になるものの大変です。

まずはシンプルなカーネルモジュールを作ってみて、少しずつ知識の範囲を広げてみるのはいかがでしょうか。何かターゲットデバイスを決めてそのドライバーを作ってみるのも良いかもしれません。ぜひ冬休みの課題のひとつとして、カーネルドライバーの作成を候補に入れてみてください。

おすすめ記事

記事・ニュース一覧