mcpField
L
linux-kernel-modules

linux-kernel-modules

github.com/mohitmishra786開発269649 回インストール18 週間前に更新
リポジトリ

linux-kernel-modules について

ロード可能な Linux カーネルモジュールを扱うエージェント向けガイドです。カーネル開発者やドライバー作成者を対象に、Kbuild、各種インターフェース、デバッグ、テスト、Secure Boot の署名を扱います。

主な機能

  1. 01Kbuild によるモジュールのビルド
  2. 02モジュールパラメーターと /proc・sysfs のインターフェース
  3. 03キャラクターデバイスの実装
  4. 04KGDB と ftrace によるデバッグ
  5. 05KUnit によるモジュールテスト
  6. 06Secure Boot 向けモジュール署名

活用シーン

  1. 01Linux カーネルモジュールを作成して読み込む
  2. 02パラメーターや sysfs インターフェースを追加する
  3. 03キャラクターデバイスドライバーを実装する
  4. 04カーネルモジュールをデバッグ、テスト、署名する

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/mohitmishra786/low-level-dev-skills --skill linux-kernel-modules

インストール後、Claude Code で/linux-kernel-modulesを入力して読み込まれたか確認します。

同梱ファイルreferences/kbuild-basics.md

詳細

Linux カーネルモジュール

目的

ロード可能な Linux カーネルモジュール(LKM)の作成方法をエージェントに案内します。対象は、Kbuild ビルドシステム、モジュールパラメーター、/proc および sysfs インターフェース、キャラクターデバイスの実装、KGDB と ftrace を使ったカーネルのデバッグ、Secure Boot 用のモジュール署名です。

トリガー

  • 「Linux カーネルモジュールの書き方を教えてください」
  • 「カーネルモジュールにパラメーターを追加する方法を教えてください」
  • 「/proc または sysfs エントリーの作成方法を教えてください」
  • 「キャラクターデバイスドライバーの実装方法を教えてください」
  • 「KGDB でカーネルモジュールをデバッグする方法を教えてください」
  • 「Secure Boot 用にカーネルモジュールへ署名する方法を教えてください」

ワークフロー

1. 最小限のカーネルモジュール
// hello.c — minimal loadable kernel module
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Your Name");
MODULE_DESCRIPTION("Minimal hello world module");
MODULE_VERSION("1.0");

static int __init hello_init(void)
{
    printk(KERN_INFO "hello: module loaded\n");
    return 0;   // non-zero = load failure
}

static void __exit hello_exit(void)
{
    printk(KERN_INFO "hello: module unloaded\n");
}

module_init(hello_init);
module_exit(hello_exit);
# Makefile — must be exactly this structure for Kbuild
obj-m := hello.o

KDIR := /lib/modules/$(shell uname -r)/build

all:
	$(MAKE) -C $(KDIR) M=$(PWD) modules

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

# Load
sudo insmod hello.ko

# Check it loaded
lsmod | grep hello
dmesg | tail -5       # see printk output

# Unload
sudo rmmod hello

# Show module info
modinfo hello.ko
2. モジュールパラメーター
#include <linux/moduleparam.h>

static int count = 1;
static char *name = "world";

// module_param(variable, type, permissions)
// permissions: 0 = no sysfs entry, S_IRUGO = readable, S_IWUSR = writable
module_param(count, int, S_IRUGO | S_IWUSR);
MODULE_PARM_DESC(count, "Number of times to print (default: 1)");

module_param(name, charp, S_IRUGO);
MODULE_PARM_DESC(name, "Name to greet (default: world)");

static int __init hello_init(void)
{
    int i;
    for (i = 0; i < count; i++)
        printk(KERN_INFO "hello: Hello, %s!\n", name);
    return 0;
}
# Pass parameters at load time
sudo insmod hello.ko count=3 name="kernel"

# Modify at runtime (if S_IWUSR set)
echo 5 > /sys/module/hello/parameters/count
3. /proc ファイルシステムインターフェース
#include <linux/proc_fs.h>
#include <linux/seq_file.h>

static struct proc_dir_entry *proc_entry;

static int mymod_show(struct seq_file *m, void *v)
{
    seq_printf(m, "Counter: %d\n", my_counter);
    seq_printf(m, "Status: %s\n", my_status ? "active" : "idle");
    return 0;
}

static int mymod_open(struct inode *inode, struct file *file)
{
    return single_open(file, mymod_show, NULL);
}

static const struct proc_ops mymod_fops = {
    .proc_open    = mymod_open,
    .proc_read    = seq_read,
    .proc_lseek   = seq_lseek,
    .proc_release = single_release,
};

static int __init mymod_init(void)
{
    proc_entry = proc_create("mymod", 0444, NULL, &mymod_fops);
    if (!proc_entry)
        return -ENOMEM;
    return 0;
}

static void __exit mymod_exit(void)
{
    proc_remove(proc_entry);
}
cat /proc/mymod
4. sysfs インターフェース
#include <linux/kobject.h>
#include <linux/sysfs.h>

static struct kobject *mymod_kobj;
static int mymod_value = 42;

static ssize_t value_show(struct kobject *kobj,
                          struct kobj_attribute *attr, char *buf)
{
    return sprintf(buf, "%d\n", mymod_value);
}

static ssize_t value_store(struct kobject *kobj,
                           struct kobj_attribute *attr,
                           const char *buf, size_t count)
{
    sscanf(buf, "%d", &mymod_value);
    return count;
}

static struct kobj_attribute value_attr =
    __ATTR(value, 0664, value_show, value_store);

static int __init mymod_init(void)
{
    mymod_kobj = kobject_create_and_add("mymod", kernel_kobj);
    if (!mymod_kobj) return -ENOMEM;
    return sysfs_create_file(mymod_kobj, &value_attr.attr);
}

static void __exit mymod_exit(void)
{
    sysfs_remove_file(mymod_kobj, &value_attr.attr);
    kobject_put(mymod_kobj);
}
cat /sys/kernel/mymod/value
echo 100 > /sys/kernel/mymod/value
5. キャラクターデバイス
#include <linux/cdev.h>
#include <linux/fs.h>
#include <linux/uaccess.h>

#define DEVICE_NAME "mydev"
#define BUF_SIZE 1024

static int major;
static struct cdev my_cdev;
static char kernel_buf[BUF_SIZE];

static int mydev_open(struct inode *inode, struct file *file) { return 0; }
static int mydev_release(struct inode *inode, struct file *file) { return 0; }

static ssize_t mydev_read(struct file *f, char __user *buf, size_t len, loff_t *off)
{
    size_t to_copy = min(len, (size_t)BUF_SIZE);
    if (copy_to_user(buf, kernel_buf, to_copy)) return -EFAULT;
    return to_copy;
}

static ssize_t mydev_write(struct file *f, const char __user *buf, size_t len, loff_t *off)
{
    size_t to_copy = min(len, (size_t)(BUF_SIZE - 1));
    if (copy_from_user(kernel_buf, buf, to_copy)) return -EFAULT;
    kernel_buf[to_copy] = '\0';
    return to_copy;
}

static const struct file_operations mydev_fops = {
    .owner   = THIS_MODULE,
    .open    = mydev_open,
    .release = mydev_release,
    .read    = mydev_read,
    .write   = mydev_write,
};

static int __init mydev_init(void)
{
    major = register_chrdev(0, DEVICE_NAME, &mydev_fops);
    if (major < 0) return major;
    printk(KERN_INFO "mydev: registered with major %d\n", major);
    return 0;
}
# Create device node (after loading module)
sudo mknod /dev/mydev c $(cat /proc/devices | grep mydev | awk '{print $1}') 0
echo "test" > /dev/mydev
cat /dev/mydev
6. KGDB と ftrace を使ったデバッグ
# KGDB — kernel GDB via serial/network
# Boot with: kgdboc=ttyS0,115200 kgdbwait
# Or over network: [email protected]/,@192.168.1.11/

# On debug host:
gdb vmlinux
(gdb) target remote /dev/ttyS0
(gdb) set architecture i386:x86-64:intel
(gdb) info registers

# ftrace — kernel function tracer
echo function > /sys/kernel/debug/tracing/current_tracer
echo mymod_write > /sys/kernel/debug/tracing/set_ftrace_filter
echo 1 > /sys/kernel/debug/tracing/tracing_on
cat /sys/kernel/debug/tracing/trace

# Dynamic debug — enable pr_debug() output
echo "module hello +p" > /sys/kernel/debug/dynamic_debug/control
7. EXPORT_SYMBOL と EXPORT_SYMBOL_GPL の違い
// EXPORT_SYMBOL — any module can link (including proprietary)
EXPORT_SYMBOL(my_helper);

// EXPORT_SYMBOL_GPL — only GPL-compatible modules can link
EXPORT_SYMBOL_GPL(gpl_only_fn);

GPL のみで利用できるカーネル内部機能に触れるシンボルには、EXPORT_SYMBOL_GPL を使用してください。GPL 以外のモジュールが GPL シンボルをインポートすると、taint 警告が出てロードに失敗します。insmod の実行後に modinfo と dmesg で確認してください。

8. KUnit によるカーネル内ユニットテスト
// test_mymod.c — compile into module or standalone KUnit module
#include <kunit/test.h>
#include "mymod_internal.h"   // functions under test

static void test_parse_valid(struct kunit *test)
{
    KUNIT_EXPECT_EQ(test, mymod_parse("42"), 42);
}

static struct kunit_case mymod_cases[] = {
    KUNIT_CASE(test_parse_valid),
    {}
};

static struct kunit_suite mymod_suite = {
    .name = "mymod",
    .test_cases = mymod_cases,
};
kunit_test_suite(mymod_suite);
# In-tree KUnit run (kernel tree with CONFIG_KUNIT=y)
./tools/testing/kunit/kunit.py run --filter mymod

# Out-of-tree: enable CONFIG_KUNIT in test kernel or use kunit module target

KUnit、kselftest、syzkaller のワークフロー全体については、skills/kernel/kernel-testing を参照してください。

9. Rust カーネルモジュール(kernel 6.x + CONFIG_RUST=y)

CONFIG_RUST=y を有効にしてカーネルをビルドし、カーネルツリーで固定されている適切な Rust ツールチェーンを使用する必要があります。

// drivers/rust_example/lib.rs
use kernel::prelude::*;

module! {
    type: RustExample,
    name: "rust_example",
    author: "You",
    description: "Rust LKM example",
    license: "GPL",
}

struct RustExample;

impl kernel::Module for RustExample {
    fn init(_module: &'static ThisModule) -> Result<Self> {
        pr_info!("Rust kernel module loaded\n");
        Ok(RustExample)
    }
}
# Build from kernel tree with Rust support enabled
make LLVM=1 rustavailable   # verify Rust toolchain
make M=drivers/rust_example modules
sudo insmod drivers/rust_example/rust_example.ko
10. モジュール署名(Secure Boot、kernel 6.x)

CONFIG_MODULE_SIG が有効な場合(Secure Boot を使用するほとんどのディストリビューションのカーネルではデフォルトで有効です)、kernel 6.x ではモジュール署名の検証が強制されます。

# Generate signing key (use org PKI in production)
openssl req -new -x509 -newkey rsa:4096 \
    -keyout signing_key.pem -out signing_cert.pem \
    -days 3650 -subj "/CN=Kernel Module Signing/" -nodes

# Sign module (sha256 or sha512 per kernel config)
/usr/src/linux-headers-$(uname -r)/scripts/sign-file \
    sha256 signing_key.pem signing_cert.pem hello.ko

# Verify signature embedded in .ko
modinfo hello.ko | grep signer

# Enroll key for Secure Boot (MOK on UEFI)
sudo mokutil --import signing_cert.pem
# Reboot → MOK Manager → enroll → reboot again

# Kernel lockdown mode check
cat /sys/kernel/security/lockdown

ロックダウンが integrity または confidentiality の場合、署名されていないモジュールは拒否されます。ディストリビューションのカーネルでは、カーネル組み込みの信頼済みキーリングに登録された鍵も必要になる場合があります(CONFIG_SYSTEM_TRUSTED_KEYS)。

Kbuild システムの詳細については、references/kbuild-basics.md を参照してください。

関連するスキル

  • KUnit および kselftest のハーネスについては、skills/kernel/kernel-testing を使用してください
  • 最小限のモジュールを超えるキャラクター/プラットフォームドライバーのパターンについては、skills/kernel/device-drivers を使用してください
  • kgdb、ftrace、kprobes については、skills/kernel/kernel-debugging を使用してください
  • モジュールを使わずにユーザー空間からカーネルをトレースするには、skills/observability/ebpf を使用してください
  • KGDB での GDB セッション管理については、skills/debuggers/gdb を使用してください
  • モジュールの ELF 構造を調査するには、skills/binaries/elf-inspection を使用してください

コメント