🊀 Rust で Kernel module をビルドしお動かす環境を䜜る

抂芁

本蚘事では、Rust で曞いた Linux kernel module をビルドしお動かすための環境を構築したす。 ゎヌルは、以䞋の Rust module を miscdrv.ko ずしおビルドしお、VM 䞊で insmod するずころたでです。

use kernel::prelude::*;

module! {
    type: MiscDrv,
    name: "miscdrv",
    authors: ["ymgyt"],
    description: "Rust misc device lab",
    license: "GPL",
}

struct MiscDrv;

impl kernel::Module for MiscDrv {
    fn init(_module: &'static ThisModule) -> Result<Self> {
        pr_info!("loaded\n");
        Ok(Self)
    }
}

impl Drop for MiscDrv {
    fn drop(&mut self) {
        pr_info!("unloaded\n");
    }
}

最終的には、VM 䞊で以䞋のように module を load/unload できるようになりたす。

root@localhost:~# insmod /mnt/drivers/miscdrv.ko
[ 5930.423638] miscdrv: loaded
root@localhost:~# rmmod miscdrv
[ 5982.685953] miscdrv: unloaded

ビルドは host で行い、実行は VM 䞊で行いたす。 VM は KVM/QEMU で動かし、libvirt で管理したす。KVM/QEMU/libvirt に぀いおは前提の確認の節で簡単に觊れたすが、より詳しくは Mastering KVM Virtualization がオススメです。

kernel module を䜜るのが初めおの方を想定しおいたす。

本蚘事のコヌドはすべお ymgyt/drivers repo で確認できたす。qemu や rustc ずいった䟝存はすべお Nix で宣蚀しおおり、開発環境の節で準備したす。

前提の確認

Kernel module ずは

kernel module ずは、実行䞭の kernel に実行コヌドを動的に远加する仕組みです。 kernel のビルドシステム(Makefile)に module を䜜成するためのレシピ(target)が甚意されおいたす。 本蚘事では、miscdrv.rs から miscdrv.ko を生成したす。 .ko の実䜓は ELF Relocatable file(ET_REL)です。

❯ readelf --file-header miscdrv.ko | rg Type:
  Type:                              REL (Relocatable file)

[f]init_module() ずいう module を load するための system call があり、insmod も最終的に finit_module() を呌んでいたす。

kernel module は kernel ず同じアドレス空間・同じ特暩レベルで動䜜したす。 module からは kernel 本䜓が EXPORT_SYMBOL[_GPL] した関数を呌ぶこずができたす。

これらのいわば内郚 API には、memory 割圓凊理(kmalloc())や各皮 subsystem ぞの登録凊理(misc_register() など)が含たれおいたす。 ただし、互換性に぀いおは、userspace(system call、ABI)ずは異なり、䞀切保蚌されたせん(stable-api-nonsense)。 したがっお、ビルド時に想定しおいた foo() が load 時の kernel に同じ signature で存圚する保蚌はありたせん。

そのため、ビルド時に参照しおいた関数(symbol)のメタデヌタ(CRC)を .ko に埋め蟌んでおき、load 時に怜蚌する仕組みがありたす。 このメタデヌタを埋め蟌むために、module のビルド前に kernel image をビルドし、Module.symvers file を生成しおおく必芁がありたす。

以䞋のように、実際に module に埋め蟌たれおいる printk の CRC 0xca2eebaa は、ビルド時に参照した kernel の Module.symvers の倀ず䞀臎しおいたした。

❯ modprobe --show-modversions handson/miscdrv/module/miscdrv.ko
0xca2eebaa      _RNvNtCse297zByDW5v_6kernel5print11call_printk
0xd272d446      __x86_return_thunk
0x7406cc83      module_layout

❯ rg _RNvNtCse297zByDW5v_6kernel5print11call_printk .build/rust-next/Module.symvers
4821:0xca2eebaa _RNvNtCse297zByDW5v_6kernel5print11call_printk  vmlinux EXPORT_SYMBOL_GPL

modprobe の src をみるず、ELF の __versions section からこれらの情報を取埗しおいるこずがわかりたす。

以䞊より、kernel module をビルドするには、load 先ずなる kernel を先にビルドしお Module.symvers を生成しおおく必芁がありたす。

Kernel config ずは

次に kernel をビルドする際の蚭定方法に぀いおです。 kernel の゜ヌスコヌドには以䞋のように CONFIG_XXX による conditional compile がありたす。

struct task_struct {
/* ... */
#ifdef CONFIG_MEM_ALLOC_PROFILING
	struct alloc_tag		*alloc_tag;
#endif
/* ... */

Rust では #[cfg()] が䜿われたす。 以䞋は抂芁に茉せた module! マクロの展開結果の䞀郚(rust/macros/module.rs が生成するコヌド)で、MODULE は「module ずしおビルドされおいるか」を衚す cfg です。

#[cfg(MODULE)]
static THIS_MODULE: ::kernel::ThisModule = unsafe {
    extern "C" {
        static __this_module: ::kernel::types::Opaque<::kernel::bindings::module>;
    };

    ::kernel::ThisModule::from_ptr(__this_module.get())
};

#[cfg(not(MODULE))]
static THIS_MODULE: ::kernel::ThisModule = unsafe {
    ::kernel::ThisModule::from_ptr(::core::ptr::null_mut())
};

同じ゜ヌスコヌドが、module ずしおビルドされる堎合は kernel が load 時に甚意する __this_module を参照し、kernel 本䜓に組み蟌たれる堎合(built-in)は null を䜿う、ずいうように分岐しおいたす。

kernel をビルドする際には、最終的には党おの蚭定を解決した .config を甚意したす。 .config の項目は on/off だけではなく、倚くの機胜が y/m/n の 3 倀(tristate)をずりたす。

  • y: kernel image に組み蟌む(built-in)
  • m: module(.ko)ずしおビルドし、実行時に load する
  • n: ビルドしない

䟋えば virtio の block driver は、Debian の config では module ですが、本蚘事で最終的に䜿う config では kernel に組み蟌んでいたす。

❯ rg '^CONFIG_VIRTIO_BLK=' dev/kernel/config/debian-13-amd64 .build/rust-next/.config

.build/rust-next/.config
1968:CONFIG_VIRTIO_BLK=y

dev/kernel/config/debian-13-amd64
2691:CONFIG_VIRTIO_BLK=m

Debian のような汎甚 distro は、幅広いハヌドりェアに察応し぀぀、䜿わない機胜を memory に茉せないために、可胜な限り m を遞ぶ方針をずっおいたす。実際、Debian の config では蚭定されおいる 6,896 項目のうち 4,049 項目が =m でした。 どの機胜を y にし、どれを m のたたにするかは、埌の Kernel config の䜜成の節で行う䜜業の䞭心になりたす。

.config で蚭定できる項目は膚倧でれロから党おを蚭定しお意図したビルドを実珟するのは倧倉です。 そこで本蚘事では Debian 13 の VM を甚意しお、そこで利甚されおいる .config を出発点にするアプロヌチを採りたした。

なお、How many ways are there to configure the Linux kernel? によるず、6.16の段階で、x86_64 で、32,468の蚭定項目があるそうです。

KVM/QEMU/libvirt

VM を䜜るずは、別の OS(kernel)を host 䞊の 1 ぀の process ずしお動かすこずだず考えおみたす。 ただし kernel は、自分が HW を盎接制埡できる前提で曞かれおいたす。device の register を memory address に map しお読み曞きしたり(Memory-Mapped I/O、MMIO)、仮想アドレスず物理アドレスのマッピングを実行する process に応じお曞き換えたりしたす。 userspace の process にこのような操䜜を蚱可するこずはできたせん。そこで、guest を実行しお、guest 内で凊理できない操䜜が起きたら制埡が戻っおくるような API を考えたす。

fn run_vm() {
    loop {
        match exec_guest() {
            Exit::Mmio(addr) => emulate_hw(addr),
            Exit::IoPort(port) => emulate_io(port),
            Exit::Halt => break,
        }
    }
}

CPU の仮想化支揎機胜(Intel VT-x / AMD-V)を抜象化しお、抂ね䞊蚘の機胜を提䟛しおくれるのが KVM です。 KVM は /dev/kvm ずいう device file を userspace に公開しおおり、KVM_RUN ioctl を通じお guest を実行できたす。(このあたりの話がピンずこなくおも心配しないでください、䞀床 driver を曞いおみるず腹萜ちしたす) このずき guest の呜什は、software が解釈・倉換しおいるわけではなく、実 CPU が盎接実行しおいたす。KVM は VMLAUNCH / VMRESUME 呜什(Intel の堎合)で CPU を guest 甚の実行 mode に切り替え、guest 内で凊理できない操䜜が起きるず CPU が host に制埡を戻したす(VM exit)。 VM exit で制埡が戻る先は、KVM_RUN を呌び出した userspace process ではなく、たず host kernel 内の KVM です。KVM 内で凊理できる堎合は guest を再開し、userspace での凊理が必芁な堎合に KVM_RUN が KVM_EXIT_MMIO のような exit reason ずずもに return したす。 この切り替えを行っおいるのが arch/x86/kvm/vmx/vmenter.S の __vmx_vcpu_run で、assembly で vmlaunch / vmresume を実行しおいる箇所が確認できたす。 思考実隓では memory のマッピング倉曎も問題ずしお挙げたしたが、擬䌌コヌドの loop には登堎しおいたせん。CPU の 2 段階のアドレス倉換(Intel では EPT、AMD では NPT)を利甚する堎合、通垞の CR3 の倉曎や guest page table の曎新を VM exit させる必芁がないためです。CPU は guest の仮想アドレスを、guest が管理する page table で guest の物理アドレスに倉換し、さらに host が管理する倉換衚で実際の物理アドレスに倉換したす。これにより guest kernel は、自分の page table の倉曎を KVM や QEMU に䞀぀ず぀ emulate させるこずなく process ごずのマッピングを切り替えられたす。䞀方、guest の物理アドレスに察応する二段目のマッピングが存圚しない堎合や暩限に違反した堎合は VM exit が発生するため、実際の物理アドレスぞのマッピングは host の管理䞋に留たりたす。

そしお KVM の実䜓は kernel module(kvm ず kvm_intel / kvm_amd)です。本蚘事で扱う kernel module ずいう仕組みの䞊に、仮想化基盀も実装されおいたす。

ただし、KVM だけでは完党な VM にはなりたせん。KVM が userspace に返した device 操䜜を凊理する、emulate_hw() の実装が別に必芁です。guest 内で block device driver による曞き蟌みが行われたら、host 偎でなんらかの方法で氞続化するずいった凊理が必芁です。 これを提䟛しおくれるのが QEMU です。䞊蚘の loop を回しおいるのも QEMU で、VM 1 台は host から芋るず 1 ぀の QEMU process です。本蚘事では、qcow2 file がこの氞続化先になりたす。 実際、擬䌌コヌドの loop は QEMU の kvm_cpu_exec() にほがそのたたの圢で存圚し、KVM_RUN ioctl の呌び出しず、返っおきた exit_reason による分岐(KVM_EXIT_IO、KVM_EXIT_MMIO、...)が確認できたす。

さらに、QEMU や VirtualBox、Xen などを抜象化しお、統䞀的な API(interface)で管理可胜にしおくれるのが libvirt です。 host で libvirtd ずいう daemon が動き、virsh はその client です。VM の定矩は domain XML、disk は storage pool ずいう単䜍で管理されたす。以降の節のログに出おくる --connect qemu:///system は、この libvirtd ぞの接続先を指定しおいたす。 なお、flake.nix で入るのは virsh などの client 偎なので、libvirtd は host で動かしおおく必芁がありたす(NixOS であれば virtualisation.libvirtd.enable)。

VM の管理ず KVM による vCPU 実行

そもそも仮想化ずは䜕かに぀いおは、䜜っお理解する仮想化技術 が非垞にオススメです。本蚘事で登堎する virtio の説明や実装(virtio-blk)もありたす。

virtiofs による file 共有

本蚘事では、host ず VM の file 共有に virtiofs を利甚しおいたす。

前提ずしお、FUSE(Filesystem in Userspace)ずいう仕組みがありたす。filesystem の凊理を kernel の倖の process に移譲するためのもので、open/read/write ずいった凊理の request/response 圢匏がプロトコルずしお決たっおいたす。 virtiofs は、この FUSE プロトコルを転甚したす。guest 内で mount した virtiofs ぞの凊理䟝頌を FUSE の request に serialize し、転送先を「同じマシンの userspace」ではなく「host 偎」に差し替えたものが virtiofs です。 host 偎では virtiofsd ずいう daemon(libvirt が VM 起動時に立ち䞊げたす)が request を受け、共有察象の directory 䞊で実際の file 操䜜を行っお response を返したす。guest から read すれば host の file の䞭身が返っおくるのはこのためです。

guest の virtiofs driver ず host 偎の backend ずの通信には virtio を利甚したす。virtio は、特定の physical device を再珟するのではなく、guest driver ず host backend が device request を受け枡すための共通 interface を定めおいたす。request ず buffer の䜍眮を memory 䞊の queue(virtqueue)ぞ远加し、queue が曎新されたこずだけを host 偎ぞ通知したす。耇数の request を memory 䞊でたずめお受け枡せるため、device 操䜜のたびに VM exit しお内容を emulate する必芁がありたせん。 ぀たり、VM(guest)内で実行されるこずを前提に、device に察する request/response をできるだけ memory に茉せお、host に凊理が戻る MMIO の頻床を抑える蚭蚈の device driver ずいえたす。 本蚘事では file 共有(virtiofs)のほか、disk(virtio-blk)も同じ仕組みです。埌の Kernel config の䜜成で FUSE_FS や VIRTIO_FS、VIRTIO_BLK を有効にするのは、この guest 偎 driver を kernel に組み蟌むためです。 なお、virtiofsd は QEMU ずは別の process なので、virtqueue を含む guest の memory にアクセスできるよう、VM の memory は共有可胜な方匏で確保する必芁がありたす。domain XML の memoryBacking(memfd + shared)がその蚭定です。

党䜓の流れ

䞊述の通り、kernel module をビルドするには、load 先ずなる kernel を先にビルドしおおく必芁がありたす。 たず、ビルドする kernel ですが、Rust for Linux の開発 branch である rust-next を利甚したす。Rust 関連の倉曎が mainline より先にここに取り蟌たれるため、垞に最新の倉曎を詊せるからです。 ただし、mainline でも問題ありたせん。rust-next の倉曎は merge window ごずに mainline に取り蟌たれ、rust-next 自䜓も mainline の rc 版を base にしおいるため、䞡者に倧きな乖離はありたせん。

kernel のビルドに必芁な .config は、Debian の config を出発点にしたす。このために、たず Debian VM(deb13)を䜜成し、config ず lsmod を採取したす。 lsmod には実際に load されおいる module 䞀芧が含たれおいたす。これを利甚しお、kernel ビルド時にビルドする module(driver)の数を枛らしたす。

ビルドした kernel は、VM に install する代わりに、QEMU の direct kernel boot で起動したす。 QEMU は disk 内の bootloader を経由せずに、host 䞊の kernel image を盎接 load しお boot できたす。 kernel を䜜り盎すたびに VM 内での install 䜜業が䞍芁になる䞀方、initramfs を䜿わない boot になるため、boot に必芁な driver(virtio や ext4)は module ではなく y で kernel に組み蟌んでおきたす。

最埌に、この kernel に察しお miscdrv module をビルドし、virtiofs の共有 directory 経由で VM に枡しお insmod したす。

開発環境の党䜓像

開発環境

たず、rust-next ず本蚘事の repo を clone したす。drivers 偎の task が ../linux を参照するため、2 ぀の repo は同じ directory に䞊べる前提です。Rust-for-Linux/linux は default branch が rust-next なので、branch の指定は䞍芁です。

git clone https://github.com/Rust-for-Linux/linux.git
git clone https://github.com/ymgyt/drivers.git
cd drivers
git checkout blog-r4l-setup
nix develop

開発に必芁な䟝存は flake.nix に宣蚀しおあり、nix develop で有効になりたす。内蚳は倧きく 3 ぀です。

  • VM の管理: libvirt(virsh)、QEMU、image の取埗に䜿う curl など
  • kernel のビルドに必芁な host tool 矀: make、flex、bison、bc、perl、python3 など
  • Rust for Linux の toolchain: clang/LLVM、lld、rustc、bindgen(執筆時点では clang 21.1.8、rustc 1.96.1、bindgen 0.72.1)

kernel はビルドに必芁な tool の最䜎 version を changes.rst で芏定しおいたす(執筆時点で Rust は 1.85.0、bindgen は 0.71.1 以䞊)。

flake.nix の shellHook では、環境倉数もあわせお蚭定しおいたす。

shellHook = ''
  export CC="${llvm.clang-unwrapped}/bin/clang"
  export HOSTCC="${llvm.clang}/bin/clang"
  export HOSTCXX="${llvm.clang}/bin/clang++"
  export BINDGEN="${pkgs.rust-bindgen-unwrapped}/bin/bindgen"
  export KERNEL_MAKE_ARGS="LLVM=1 CC=$CC HOSTCC=$HOSTCC HOSTCXX=$HOSTCXX RUSTC=$RUSTC BINDGEN=$BINDGEN"
  exec nu
'';

KERNEL_MAKE_ARGS は、以降の節で just が実行する make にそのたた枡されたす。ログに出おくる LLVM=1 CC=/nix/store/... RUSTC=/nix/store/... ずいう長い匕数の正䜓はこれです。 LLVM=1 は kernel を GCC ではなく clang/LLVM でビルドする指定です。Rust 偎の bindgen が libclang を利甚するため、C 偎も同じ LLVM に揃えおいたす。 CC にだけ unwrapped の clang を䜿っおいるのは Nix 特有の事情です。Nix の clang wrapper は Nix 環境向けの compile flag を暗黙に泚入するため、flag を厳密に管理する kernel 本䜓のビルドには向きたせん。䞀方、ビルド䞭に host 偎で動かす tool のビルド(HOSTCC)には、暙準 library の解決をしおくれる wrapper の方が郜合が良いためです。

toolchain が揃っおいるかは、kernel が甚意しおいる rustavailable target で確認できたす。

❯ just kernel rustavailable
make -C "<linux>" "O=<drivers>/.build/rust-next" LLVM=1 CC=/nix/store/<hash>-clang-21.1.8/bin/clang HOSTCC=/nix/store/<hash>-clang-wrapper-21.1.8/bin/clang HOSTCXX=/nix/store/<hash>-clang-wrapper-21.1.8/bin/clang++ RUSTC=/nix/store/<hash>-rustc-wrapper-1.96.1/bin/rustc BINDGEN=/nix/store/<hash>-rust-bindgen-unwrapped-0.72.1/bin/bindgen rustavailable
make: Entering directory '<linux>'
make[1]: Entering directory '<drivers>/.build/rust-next'
Rust is available!
make[1]: Leaving directory '<drivers>/.build/rust-next'
make: Leaving directory '<linux>'

なお、shellHook の最埌の exec nu により、nix develop 埌の shell は nushell になりたす。この repo の task(just)が nushell を前提にしおいるためです。

Debian VM(deb13)の䜜成

たず Debian VM を䜜成したす。host で kernel image をビルドする際の基準ずなる蚭定(config ず lsmod)を取埗するためです。

libvirt では、VM の disk などの storage を pool ずいう単䜍で管理したす。dir type の pool の実䜓はただの directory で、その䞭の file が volume ずしお扱われたす。ここでは repo 内の vm/storage/ を drivers ずいう名前の pool ずしお登録したす。

❯ just vm pool define

command: virsh --connect qemu:///system pool-define-as drivers dir --target <drivers>/vm/storage
Pool drivers defined

❯ just vm pool start
command: virsh --connect qemu:///system pool-start drivers
Pool drivers started

VM の base image ずしお利甚する Debian cloud image を取埗したす。 Debian の cloud image は、AWS などの cloud platform 䞊で cloud-init ずいう仕組みによっお初期蚭定(user の䜜成や SSH 鍵の投入など)が行われるこずを前提ずした variant が䞭心です。 今回は cloud ではなく手元の libvirt で䜿うため、cloud-init を含たず、boot 埌そのたた serial console からログむンしお䜿い始められる nocloud variant を利甚したす。

nocloud: Does not run cloud-init and boots directly to a root prompt. Useful for local VM instantiation with tools like QEMU.

❯ just vm image

image: downloading debian-13-nocloud-amd64-20260810-2566.qcow2
image: verifying SHA-512
image: checksum verified
pool: refreshing drivers
command: virsh --connect qemu:///system pool-refresh drivers
Pool drivers refreshed

volume: debian-13-nocloud-amd64-20260810-2566.qcow2
command: virsh --connect qemu:///system vol-info debian-13-nocloud-amd64-20260810-2566.qcow2 --pool drivers
Name:           debian-13-nocloud-amd64-20260810-2566.qcow2
Type:           file
Capacity:       3.00 GiB
Allocation:     387.25 MiB

取埗した base image を VM の disk ずしお盎接䜿うのではなく、base image を backing file ずする copy-on-write の volume(overlay)を VM ごずに䜜成したす。曞き蟌みは overlay 偎にのみ蚘録され、base image は倉曎されたせん。埌で䜜成する rust VM も同じ base image を共有し、それぞれ自分の overlay を持ちたす。

❯ just vm overlay deb13

volume: creating deb13.qcow2
command: virsh --connect qemu:///system vol-create-as drivers deb13.qcow2 3221225472 --format qcow2 --backing-vol debian-13-nocloud-amd64-20260810-2566.qcow2 --backing-vol-format qcow2
Vol deb13.qcow2 created

volume: deb13.qcow2
command: virsh --connect qemu:///system vol-info deb13.qcow2 --pool drivers
Name:           deb13.qcow2
Type:           file
Capacity:       3.00 GiB
Allocation:     196.00 KiB

䜜成盎埌の overlay は、Capacity 3.00 GiB に察しお Allocation(実際に確保された容量)が 196.00 KiB しかないこずから、ただほずんど䜕も曞き蟌たれおいないこずが確認できたす。

❯ just vm define deb13

domain: rendered vm/domains/deb13/domain.xml
domain: defining deb13
command: virsh --connect qemu:///system define vm/domains/deb13/domain.xml --validate
Domain 'deb13' defined from vm/domains/deb13/domain.xml

command: virsh --connect qemu:///system dominfo deb13
Id:             -
Name:           deb13
UUID:           6e62dce4-6248-4db5-bfbb-2049bcbc5dc5
OS Type:        hvm
State:          shut off
CPU(s):         2
Max memory:     2097152 KiB
Used memory:    2097152 KiB
Persistent:     yes
Autostart:      disable
Autostart Once: disable
Managed save:   no
Security model: none
Security DOI:   0

VM に接続したす。

❯ just vm boot deb13

<boot log...>

Linux localhost 6.12.101+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.101-1 (2026-08-05) x86_64

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
root@localhost:~# uname -a
Linux localhost 6.12.101+deb13-amd64 #1 SMP PREEMPT_DYNAMIC Debian 6.12.101-1 (2026-08-05) x86_64 GNU/Linux

config ず lsmod の採取

host ずの file のやりずりには virtiofs を利甚したす。domain XML で、host の vm/shared/ directory を drivers ずいう名前で guest に export しおおり、guest からは virtiofs ずしお mount するこずでアクセスできたす。

root@localhost:~# mkdir -p /mnt/drivers
root@localhost:~# mount -t virtiofs drivers /mnt/drivers
root@localhost:~# findmnt /mnt/drivers
TARGET       SOURCE  FSTYPE   OPTIONS
/mnt/drivers drivers virtiofs rw,relatime

root@localhost:~# cp /boot/config-$(uname -r) /mnt/drivers/

続けお、load されおいる module の䞀芧も採取したす。これは埌の Kernel config の䜜成で利甚したす。

lsmod > /mnt/drivers/lsmod-$(uname -r)

host 偎で、採取した config を repo の管理䞋に copy したす。

❯ file vm/shared/config-6.12.101+deb13-amd64
vm/shared/config-6.12.101+deb13-amd64: Linux make config build file, ASCII text

❯ cp vm/shared/config-6.12.101+deb13-amd64 dev/kernel/config/debian-13-amd64

Kernel config の䜜成

採取した Debian の config を出発点に、rust-next 甚の .config を䜜成したす。 kernel のビルドは source tree を汚さない build(make O=<dir>)で行い、.config を含む成果物はすべお .build/rust-next に眮きたす。たず seed ずなる config を build directory に copy したす。

❯ just kernel initconfig
mkdir -p "<drivers>/.build/rust-next"
cp --force "<drivers>/dev/kernel/config/debian-13-amd64" "<drivers>/.build/rust-next/.config"

seed は kernel 6.12 圓時の config なので、rust-next(7.2-rc1 base)の Kconfig に合わせお曎新したす。これを行うのが olddefconfig target です。

既存の .config を base に、新しく増えた項目を察話なしで default 倀に蚭定したす。

❯ just kernel olddefconfig

make -C "<linux>" "O=<drivers>/.build/rust-next" olddefconfig
make: Entering directory '<linux>'
make[1]: Entering directory '<drivers>/.build/rust-next'
.config:1420:warning: symbol value 'm' invalid for NETFILTER_NETLINK
.config:6870:warning: symbol value 'm' invalid for FB_BACKLIGHT
.config:8650:warning: symbol value 'm' invalid for HYPERV
.config:9969:warning: symbol value 'm' invalid for ANDROID_BINDER_IPC
.config:10916:warning: symbol value 'm' invalid for CRYPTO_LIB_CURVE25519_GENERIC
.config:10922:warning: symbol value 'm' invalid for CRYPTO_LIB_POLY1305_GENERIC
.config:11203:warning: symbol value 'n' invalid for BOOTPARAM_SOFTLOCKUP_PANIC
.config:11215:warning: symbol value 'n' invalid for BOOTPARAM_HUNG_TASK_PANIC
#
# configuration written to .config
#
make[1]: Leaving directory '<drivers>/.build/rust-next'
make: Leaving directory '<linux>'

warning は、6.12 から 7.2 の間に項目の型が倉わったこずを瀺しおいたす。䟋えば NETFILTER_NETLINK は tristate から bool に倉わったため m が、BOOTPARAM_SOFTLOCKUP_PANIC は bool から int に倉わったため n が、それぞれ invalid ず報告されおいたす。いずれも olddefconfig が新しい型の default 倀で蚭定し盎すので、察応は䞍芁です。

次に Rust support を有効化したす。scripts/config は .config を command line から線集するための kernel 付属の script です。

❯ just kernel rustconfig
"<linux>/scripts/config" --file "<drivers>/.build/rust-next/.config" --enable GENDWARFKSYMS --enable RUST

RUST ず同時に GENDWARFKSYMS を有効化しおいるのは、init/Kconfig に䟝存が定矩されおいるためです。

config RUST
	bool "Rust support"
	...
	select EXTENDED_MODVERSIONS if MODVERSIONS
	depends on !MODVERSIONS || GENDWARFKSYMS

前提の確認で芋た symbol の CRC は、C のコヌドでは genksyms ずいう parser が蚈算したすが、genksyms は Rust のコヌドを解析できたせん。GENDWARFKSYMS は、CRC を DWARF debug 情報から蚈算する代替の仕組みです。seed の config は CONFIG_MODVERSIONS=y なので、GENDWARFKSYMS なしでは RUST を有効にできたせん。

次に、VM の boot に必芁な driver を有効化したす。党䜓の流れで述べたずおり、direct kernel boot では initramfs を䜿わないため、boot に必芁な driver は module ではなく kernel 本䜓に組み蟌んで(=y)おく必芁がありたす。

❯ just kernel vmconfig
"<linux>/scripts/config" --file "<drivers>/.build/rust-next/.config" --enable VIRTIO_MENU --enable VIRTIO_PCI --enable VIRTIO_BLK --enable EXT4_FS --enable EFI_PARTITION --enable FUSE_FS --enable VIRTIO_FS --enable SERIAL_8250 --enable SERIAL_8250_CONSOLE --enable VFAT_FS --enable NLS_CODEPAGE_437 --enable NLS_ASCII --enable EFIVAR_FS

有効にしおいるのは、root filesystem の mount に必芁な virtio disk ず ext4(VIRTIO_PCI, VIRTIO_BLK, EXT4_FS, EFI_PARTITION)、host ずの file 共有に䜿う virtiofs(FUSE_FS, VIRTIO_FS)、serial console(SERIAL_8250, SERIAL_8250_CONSOLE)、および EFI 関連(VFAT_FS, EFIVAR_FS など)です。

最埌に、採取した lsmod を甚いお、ビルドする module を実際に load されおいたものに絞り蟌みたす。localmodconfig は .config の =m の項目のうち、LSMOD で指定した䞀芧に茉っおいない module を無効にしたす(=y の項目には觊れたせん)。yes "" を前眮しおいるのは、途䞭で聞かれる質問にすべお default で回答するためです。

❯ just kernel localmodconfig vm/shared/lsmod-6.12.101+deb13-amd64
yes "" | make -C "<linux>" "O=<drivers>/.build/rust-next" LLVM=1 CC=/nix/store/<hash>-clang-21.1.8/bin/clang HOSTCC=/nix/store/<hash>-clang-wrapper-21.1.8/bin/clang HOSTCXX=/nix/store/<hash>-clang-wrapper-21.1.8/bin/clang++ RUSTC=/nix/store/<hash>-rustc-wrapper-1.96.1/bin/rustc BINDGEN=/nix/store/<hash>-rust-bindgen-unwrapped-0.72.1/bin/bindgen "LSMOD=<drivers>/vm/shared/lsmod-6.12.101+deb13-amd64" localmodconfig
make: Entering directory '<linux>'
make[1]: Entering directory '<drivers>/.build/rust-next'

これにより、seed の時点で 4,049 個あった =m の項目は最終的に 49 個たで枛り、kernel のビルド時間が倧幅に短くなりたす。

Kernel image の build

config ができたので、kernel image をビルドしたす。

❯ just kernel image
make -C "<linux>" "O=<drivers>/.build/rust-next" LLVM=1 CC=/nix/store/<hash>-clang-21.1.8/bin/clang HOSTCC=/nix/store/<hash>-clang-wrapper-21.1.8/bin/clang HOSTCXX=/nix/store/<hash>-clang-wrapper-21.1.8/bin/clang++ RUSTC=/nix/store/<hash>-rustc-wrapper-1.96.1/bin/rustc BINDGEN=/nix/store/<hash>-rust-bindgen-unwrapped-0.72.1/bin/bindgen -j14 bzImage
make: Entering directory '<linux>'
make[1]: Entering directory '<drivers>/.build/rust-next'
  SYNC    include/config/auto.conf
  HOSTRUSTC scripts/generate_rust_target

...

Kernel: arch/x86/boot/bzImage is ready  (#4)

Rust VM の初期化

ビルドした kernel で boot する VM を䜜成したす。手順は deb13 ず同じです。同じ base image から rust 甚の overlay を䜜り、domain を定矩したす。

❯ just vm define rust
exists: vm/storage/debian-13-nocloud-amd64-20260810-2566.qcow2
image: verifying SHA-512
image: checksum verified
pool: refreshing drivers
command: virsh --connect qemu:///system pool-refresh drivers
Pool drivers refreshed

volume: debian-13-nocloud-amd64-20260810-2566.qcow2
command: virsh --connect qemu:///system vol-info debian-13-nocloud-amd64-20260810-2566.qcow2 --pool drivers
Name:           debian-13-nocloud-amd64-20260810-2566.qcow2
Type:           file
Capacity:       3.00 GiB
Allocation:     387.25 MiB

volume: creating rust.qcow2
command: virsh --connect qemu:///system vol-create-as drivers rust.qcow2 3221225472 --format qcow2 --backing-vol debian-13-nocloud-amd64-20260810-2566.qcow2 --backing-vol-format qcow2
Vol rust.qcow2 created

volume: rust.qcow2
command: virsh --connect qemu:///system vol-info rust.qcow2 --pool drivers
Name:           rust.qcow2
Type:           file
Capacity:       3.00 GiB
Allocation:     196.00 KiB

domain: rendered vm/domains/rust/domain.xml
domain: defining rust
command: virsh --connect qemu:///system define vm/domains/rust/domain.xml --validate
Domain 'rust' defined from vm/domains/rust/domain.xml

command: virsh --connect qemu:///system dominfo rust
Id:             -
Name:           rust
UUID:           5ecd39df-0b03-4cb6-9d9d-5ebed3d88a51
OS Type:        hvm
State:          shut off
CPU(s):         2
Max memory:     2097152 KiB
Used memory:    2097152 KiB
Persistent:     yes
Autostart:      disable
Autostart Once: disable
Managed save:   no
Security model: none
Security DOI:   0

deb13 ずの実質的な違いは、domain XML の <os> だけです。

   <os firmware='efi'>
     <type arch='x86_64' machine='pc-q35-10.2'>hvm</type>
-    <boot dev='hd'/>
+    <kernel>${KERNEL_IMAGE}</kernel>
+    <cmdline>root=PARTUUID=6cf4a790-ccd9-46ad-a256-5f853165c65e ro console=tty0 console=ttyS0,115200 earlyprintk=ttyS0,115200 consoleblank=0</cmdline>
   </os>

deb13 の <boot dev='hd'/> は disk から boot する指定です。EFI firmware が disk 内の bootloader を起動し、bootloader が disk に install された Debian の kernel ず initramfs を load したす。 rust VM ではこれを <kernel> に眮き換えおいたす。これが QEMU の direct kernel boot で、bootloader を経由せずに、QEMU が host 䞊の kernel image を盎接 load しお起動したす。${KERNEL_IMAGE} は domain XML の render 時に .build/rust-next/arch/x86/boot/bzImage に展開されるので、kernel をビルドし盎したら VM を起動し盎すだけで新しい kernel が boot したす。

<cmdline> は、本来 bootloader が kernel に枡す command line を自分で指定するものです。 root=PARTUUID=... は root filesystem の堎所を瀺したす。initramfs がないため、kernel は自力でこの partition を芋぀けお mount できる必芁がありたす。Kernel config の䜜成で VIRTIO_BLK や EXT4_FS を =y にしたのは、この䞀行のためです。 PARTUUID の倀は deb13 内で blkid を実行するず確認できたす。

root@localhost:~# blkid
/dev/vda15: SEC_TYPE="msdos" UUID="2041-A3EB" BLOCK_SIZE="512" TYPE="vfat" PARTUUID="e65fb8b6-cb05-404f-ba6f-37e2770a43cf"
/dev/vda1: UUID="ed2d1185-cebf-4062-a788-eeab651e6ad2" BLOCK_SIZE="4096" TYPE="ext4" PARTUUID="6cf4a790-ccd9-46ad-a256-5f853165c65e"
/dev/vda14: PARTUUID="f6363e47-3332-4fac-8258-9f606b790ff1"

root filesystem である ext4 partition(/dev/vda1)の PARTUUID が cmdline に指定した倀です。deb13 ず rust は同じ base image から䜜った overlay なので、同じ倀になりたす。PARTUUID は image の build ごずに倉わるため、別の image で远詊する堎合は読み盎す必芁がありたす。 ちなみに vfat の partition(/dev/vda15)は EFI System Partition で、Kernel config の䜜成で VFAT_FS を有効にしたのは、boot 埌にこれを mount できるようにするためです。 console=ttyS0,115200 は kernel log の出力先を serial console にする指定で、virsh console で芋えるのはこの出力です。

❯ just vm boot rust

root@localhost:~# uname -a
Linux localhost 7.2.0-rc1+ #4 SMP PREEMPT_DYNAMIC Sat Aug 15 13:21:42 JST 2026 x86_64 GNU/Linux

Module の build

いよいよ miscdrv をビルドしたす。その前に、in-tree の module をビルドしおおきたす。

❯ just kernel modules

前提の確認で芋たずおり、out-of-tree module のビルドには、kernel が export する symbol の CRC 台垳である Module.symvers が必芁です。bzImage のビルドで生成されるのは vmlinux 分(vmlinux.symvers)たでで、Module.symvers はこの modules target で生成されたす。

miscdrv 偎の Makefile は以䞋のずおりです。

ifneq ($(KERNELRELEASE),)
obj-m := miscdrv.o
else
KDIR ?= ../../../.build/rust-next

.PHONY: modules modules_prepare clean

modules: modules_prepare
	$(MAKE) -C "$(KDIR)" M="$$PWD" $(KERNEL_MAKE_ARGS) modules

modules_prepare:
	$(MAKE) -C "$(KDIR)" $(KERNEL_MAKE_ARGS) modules_prepare

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

out-of-tree module のビルドは、kernel のビルドシステム(kbuild)ぞの䟝頌ずいう圢をずりたす(Documentation/kbuild/modules)。make -C <kernel build directory> M=<module の directory> modules で、「M= にある module をビルドせよ」ず䌝えたす。 この Makefile は 2 回読たれたす。手元で make modules を実行するず else 偎が評䟡され、kbuild に䟝頌が飛びたす。kbuild が M= の directory を凊理する際に再びこの Makefile を読み(このずきは KERNELRELEASE が定矩されおいたす)、今床は obj-m := miscdrv.o だけが評䟡されたす。 obj-m は module ずしおビルドする object の宣蚀です。kbuild は miscdrv.o の source ずしお miscdrv.rs を芋぀け、rustc でコンパむルしお miscdrv.ko たで組み立おたす。

❯ cd handson/miscdrv/module/

❯ make modules

❯ modinfo miscdrv.ko
filename:       <drivers>/handson/miscdrv/module/miscdrv.ko
author:         ymgyt
description:    Rust misc device lab
license:        GPL
name:           miscdrv
depends:
vermagic:       7.2.0-rc1+ SMP preempt mod_unload modversions
retpoline:      Y

出力の vermagic に泚目しおください。kernel release ず䞻芁な構成(SMP、preempt、modversions)が文字列ずしお module に埋め蟌たれおおり、load 時に kernel 自身の倀ず比范され、䞀臎しなければ拒吊されたす。前提の確認で芋た symbol 単䜍の CRC 照合ず、この vermagic の 2 段構えで、module が load 先の kernel ず敎合しおいるこずが怜査されたす。

Module の実行

ビルドした miscdrv.ko を virtiofs の共有 directory に眮き、VM 内から insmod したす。

❯ cd ../../..

❯ cp handson/miscdrv/module/miscdrv.ko vm/shared

❯ just vm console rust

root@localhost:~# mkdir -p /mnt/drivers
root@localhost:~# mount -t virtiofs drivers /mnt/drivers
root@localhost:~# ls /mnt/drivers
config-6.12.101+deb13-amd64  lsmod-6.12.101+deb13-amd64  miscdrv.ko

root@localhost:~# insmod /mnt/drivers/miscdrv.ko
[ 5930.419675] miscdrv: loading out-of-tree module taints kernel.
[ 5930.421134] miscdrv: module verification failed: signature and/or required key missing - tainting kernel
[ 5930.423638] miscdrv: loaded

root@localhost:~# lsmod | grep miscdrv
miscdrv                12288  0
root@localhost:~# rmmod miscdrv
[ 5982.685953] miscdrv: unloaded

insmod 時の最初の 2 行は warning ですが、想定どおりのものです。1 行目の taint は「この kernel は tree 倖の code を含んでいる」ずいう印で、out-of-tree module を load するず必ず付きたす。2 行目は module の眲名怜蚌に倱敗したずいうもので、自分でビルドした未眲名の module なのでこれも想定どおりです。 そしお 3 行目の miscdrv: loaded が、抂芁に茉せた init() の pr_info! の出力です。rmmod するず Drop が呌ばれ、unloaded が出力されたした。

これで぀いに、kernel module をビルドしお VM 内で怜蚌する流れができたした。

たずめ

最初は、module! の仕組み(なんず kernel 内で syn、quote が䜿えたす)を曞こうずしおいたのですが、環境構築の章が長くなったので蚘事を分けるこずにしたした。 Rust から kernel の各皮機胜(memory 割圓、lock 取埗、timer、subsystem 登録、...)ぞのアクセスを提䟛する kernel crate は絶賛開発䞭で、日々機胜远加が続いおいたす。 Rust for Linux project のおかげで、今たで C 開発者の特暩だった module/driver 開発を Rust からも行えるのは非垞にありがたいです。 ちなみに、kernel 内の Rust に察するスタンスは subsystem ごずに様々です。

DRM subsystem では 2025 幎 12 月の段階で

It was still perhaps surprising, though, when Airlie (the DRM maintainer) said that the subsystem is only "about a year away" from disallowing new drivers written in C and requiring the use of Rust.

(それでもやはり驚きだったのは、DRM subsystem の maintainer である Airlie 氏が、C で曞かれた新しい driver を受け入れず Rust の䜿甚を必須ずするのは「あず 1 幎皋床」先の話だ、ず述べたこずです。)

ずいう話が、LWN で玹介されおいたした。