# 機能設計書 6-カーネルモジュール管理

## 概要

本ドキュメントは、FreeBSDにおけるカーネルモジュール（KLD: Kernel Linker）管理機能の設計を記述する。カーネルモジュールの動的ロード・アンロード・状態確認を対象とする。

### 本機能の処理概要

**業務上の目的・背景**：カーネルモジュール管理は、カーネル再コンパイルなしに機能を動的に追加・削除できる仕組みを提供する。デバイスドライバ、ファイルシステム、ネットワークプロトコルなどをモジュールとして必要に応じてロード・アンロードでき、システムの柔軟性と保守性を向上させる。

**機能の利用シーン**：デバイスドライバのオンデマンドロード（devdによる自動ロード）、ファイルシステムモジュールのロード（例: zfs.ko）、ネットワークモジュールの追加（例: if_bridge.ko）、開発時のドライバテスト、サーバ設定変更時のモジュール入れ替えで利用される。

**主要な処理内容**：
1. **kldload**: カーネルモジュールファイル（.ko）をメモリにロードし、カーネルリンカにより未解決シンボルを解決してカーネルに統合する。
2. **kldunload**: ロード済みモジュールをカーネルから切り離し、メモリを解放する。モジュールの参照カウントが0であることを確認する。
3. **kldstat**: 現在ロードされているモジュールの一覧と詳細情報（アドレス、サイズ、参照カウント等）を表示する。
4. **モジュールパス管理**: kern.module_path sysctlパラメータによるモジュール検索パスの管理。

**関連システム・外部連携**：devdデバイスイベントデーモン（自動モジュールロード）、bootloaderからのモジュールロード、sysctl（kern.module_path）、securelevelによる制限との連携。

**権限による制御**：kldload/kldunloadの実行にはスーパーユーザ権限（root）が必要。securelevel > 0ではモジュールのロード・アンロードが禁止される。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | 該当なし | - | カーネルモジュール管理はCLI操作であり、直接的なGUI画面は存在しない |

## 機能種別

カーネル基盤機能（動的モジュールロード・アンロード）

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| kldload引数 | char * | Yes | モジュールファイル名またはパス | ファイル存在チェック |
| kldunload引数 | int/char * | Yes | モジュールIDまたは名前 | ロード済みモジュールであること |
| -n フラグ（kldload） | - | No | 重複ロードを許容しない（デフォルト動作） | - |
| -v フラグ（kldload） | - | No | 詳細表示 | - |

### 入力データソース

kldload/kldunload/kldstatコマンドのCLI引数、またはkld_load/kld_unloadシステムコール経由。

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| kldload戻り値 | int | 成功時はモジュールID、失敗時は-1 |
| kldstat出力 | テキスト | Id Refs Address Size Name 形式のテーブル |

### 出力先

標準出力（kldstatの場合）、システムログ（モジュールロード・アンロードイベント）。

## 処理フロー

### 処理シーケンス

```
1. kldload処理
   ├─ kldload(1)コマンド実行
   │    ├─ path_check(): モジュールパス検索（kern.module_path参照）
   │    ├─ kldload()システムコール呼び出し
   │    │    ├─ linker_load_file(): .koファイル読み込み
   │    │    ├─ リンカによるシンボル解決
   │    │    ├─ MOD_LOAD呼び出し（モジュール初期化）
   │    │    └─ ロード済みリストへの追加
   │    └─ エラー時: エラーメッセージ表示

2. kldunload処理
   ├─ kldunload(1)コマンド実行
   │    ├─ kldunload()システムコール呼び出し
   │    │    ├─ 参照カウントチェック
   │    │    ├─ MOD_UNLOAD呼び出し（モジュール終了処理）
   │    │    └─ メモリ解放・リストからの削除
   │    └─ エラー時: エラーメッセージ表示

3. kldstat処理
   └─ kldstat()システムコールでロード済みモジュール一覧取得
```

### フローチャート

```mermaid
flowchart TD
    A[kldload コマンド] --> B[path_check: モジュール検索]
    B --> C{ファイル発見?}
    C -->|No| D[エラー: モジュールが見つからない]
    C -->|Yes| E[kldload システムコール]
    E --> F[linker_load_file]
    F --> G[シンボル解決]
    G --> H{解決成功?}
    H -->|No| I[エラー: 未解決シンボル]
    H -->|Yes| J[MOD_LOAD: モジュール初期化]
    J --> K[ロード完了]
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | root権限必須 | kldload/kldunloadにはroot権限が必要 | 全ロード・アンロード操作時 |
| BR-02 | securelevel制限 | securelevel > 0ではモジュール操作禁止 | securelevelが設定されている場合 |
| BR-03 | 重複ロード防止 | 既にロード済みのモジュールは再ロードされない | kldload -nフラグ（デフォルト） |
| BR-04 | 参照カウント | 参照カウントが0でないモジュールはアンロード不可 | kldunload実行時 |
| BR-05 | モジュールパス | kern.module_pathの順序でモジュールを検索 | パス指定なしのロード時 |

### 計算ロジック

path_check()（kldload.c 48行目）では、モジュール名にパス区切り（/）が含まれない場合、kern.module_path sysctlで取得したパスリストを順に検索し、同名ファイルの存在を確認する。

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象データ構造 | 操作種別 | 概要 |
|-----|-------------|---------|------|
| kldload | linker_files リスト | INSERT | ロードしたモジュール情報の追加 |
| kldunload | linker_files リスト | DELETE | モジュール情報の削除 |
| kldstat | linker_files リスト | SELECT | ロード済みモジュール一覧の取得 |

### テーブル別操作詳細

#### linker_file構造体

| 操作 | 項目（フィールド名） | 更新値・取得条件 | 備考 |
|-----|-----------------|-----------------|------|
| INSERT | filename | モジュールファイル名 | .ko拡張子 |
| INSERT | address | ロードアドレス | カーネル仮想アドレス空間内 |
| INSERT | size | モジュールサイズ | バイト単位 |
| UPDATE | refs | 参照カウント | ロード時1、依存関係で増加 |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| EPERM | 権限不足 | 非rootでのロード/アンロード | root権限で実行 |
| ENOENT | ファイル不存在 | モジュールファイルが見つからない | パスを確認・kern.module_pathを確認 |
| ENOEXEC | 実行不可 | モジュール形式不正 | 正しいモジュールファイルを指定 |
| EBUSY | 使用中 | 参照カウント > 0でのアンロード | 依存モジュールを先にアンロード |
| EEXIST | 重複 | 既にロード済み | -fフラグで強制ロードまたは不要 |

### リトライ仕様

モジュールロード失敗時はエラーメッセージを表示して終了。ユーザが原因を解決した後に再試行する。

## トランザクション仕様

モジュールロードはlinker_load_file()内で段階的に行われ、シンボル解決失敗時にはロード途中のリソースが適切に解放される。MOD_LOADコールバックが失敗した場合もロードがロールバックされる。

## パフォーマンス要件

- モジュールロード: ファイルI/O + シンボル解決のため数十ミリ秒〜数百ミリ秒
- kldstat: メモリ内リスト走査のためマイクロ秒オーダー

## セキュリティ考慮事項

- securelevelによるモジュール操作の制限（本番環境での不正モジュールロード防止）
- mac_kld(4)によるMACポリシーの適用
- ロードするモジュールの整合性検証（署名検証はveriexecモジュールで提供可能）

## 備考

- kldload.cの41行目で`#define PATHCTL "kern.module_path"`が定義されている
- path_check()はファイル名のみ指定された場合にモジュールパス上の同名ファイルとの競合を警告する
- ブートローダ（loader）もKLD形式モジュールをロードでき、カーネルと同じモジュールが使用される

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | linker.h | `sys/sys/linker.h` | struct linker_fileの定義。filename、address、size、refs等 |
| 1-2 | module.h | `sys/sys/module.h` | struct module定義。modevent_t（MOD_LOAD/MOD_UNLOAD等）の列挙 |

**読解のコツ**: FreeBSDのKLDシステムは「linker_file」（ロードされたオブジェクトファイル）と「module」（その中の個別モジュール）の二層構造になっている。1つの.koファイルに複数のmoduleが含まれることがある。

#### Step 2: ユーザ空間コマンドを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | kldload.c | `sbin/kldload/kldload.c` | path_check()（48行目）とmain()関数。kern.module_pathの参照 |
| 2-2 | kldunload.c | `sbin/kldunload/kldunload.c` | アンロード処理。-fフラグによる強制アンロード |
| 2-3 | kldstat.c | `sbin/kldstat/kldstat.c` | モジュール一覧表示。-v詳細表示オプション |

**主要処理フロー**:
1. **41行目（kldload.c）**: PATHCTLマクロで"kern.module_path"を定義
2. **48-60行目（kldload.c）**: path_check()がファイル名にパス区切りがない場合にモジュールパス検索

#### Step 3: カーネル側リンカを理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | kern_linker.c | `sys/kern/kern_linker.c` | カーネルリンカ主要実装。linker_load_file()、linker_unload_file() |
| 3-2 | kern_module.c | `sys/kern/kern_module.c` | モジュール管理。module_register()、module_unregister() |
| 3-3 | link_elf.c | `sys/kern/link_elf.c` | ELF形式モジュールのロード処理 |

### プログラム呼び出し階層図

```
kldload(1) コマンド
    |
    +-- path_check() [パス競合チェック]
    +-- kldload() syscall
            |
            +-- linker_load_file()
                    +-- link_elf_load_file() [ELFロード]
                    +-- linker_file_register_sysctls()
                    +-- module_init() → MOD_LOAD

kldunload(1) コマンド
    |
    +-- kldunload() syscall
            |
            +-- linker_file_unload()
                    +-- module_fini() → MOD_UNLOAD
                    +-- linker_file_sysuninit()
                    +-- free() [メモリ解放]

kldstat(1) コマンド
    |
    +-- kldnext()/kldstat() syscall
            +-- linker_files リスト走査
```

### データフロー図

```
[入力]                  [処理]                         [出力]

モジュール名 ────> path_check() ──────────────────> パス解決
                   kldload() syscall
                   linker_load_file() ────────────> モジュールID
                   MOD_LOAD コールバック              カーネルへの統合

モジュールID ────> kldunload() syscall ────────────> モジュール除去
                   MOD_UNLOAD コールバック             メモリ解放

(なし) ──────────> kldstat() syscall ──────────────> モジュール一覧表示
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| kldload.c | `sbin/kldload/kldload.c` | ソース | kldloadコマンド実装 |
| kldunload.c | `sbin/kldunload/kldunload.c` | ソース | kldunloadコマンド実装 |
| kldstat.c | `sbin/kldstat/kldstat.c` | ソース | kldstatコマンド実装 |
| kern_linker.c | `sys/kern/kern_linker.c` | ソース | カーネルリンカ主要実装 |
| kern_module.c | `sys/kern/kern_module.c` | ソース | モジュール管理 |
| link_elf.c | `sys/kern/link_elf.c` | ソース | ELFモジュールローダ |
| linker.h | `sys/sys/linker.h` | ヘッダ | linker_file構造体定義 |
| module.h | `sys/sys/module.h` | ヘッダ | module構造体・modevent_t定義 |
| kldload.8 | `sbin/kldload/kldload.8` | manページ | kldloadコマンドマニュアル |
