Linux カーネルを新しいプラットフォームでビルドする手順

2026/09/04 2:57

Linux カーネルを新しいプラットフォームでビルドする手順

RSS: https://news.ycombinator.com/rss

要約

日本語翻訳:

本記事では、カスタム物理ハードウェアを使用せず、完全な Linux カーネルをユーザー空間で起動することに成功した、約 2,000 行の C++ コードで構築された最小限の RISC-V エミュレーターについて説明している。通常のセットアップが U-Boot などのブートローダーを使用してストレージからファイルをロードするのとは異なり、このエミュレーターは重要な周辺機器(RAM、UART8250)およびリソースを特定のアドレスに直接メモリマッピングする。これらは RV32IMA 命令セットをサポートし、タイマーについては SBI を介してインターフェースしており、MMU を備えた CPU、十分な RAM、およびシステムクロックタイマーが必要とされる。

Linux を起動するには、エミュレーターバイナリ内にカーネルイメージ、デバイスツリーブロッブ(DTB)、およびイニトラムフス(initramfs)の 3 つのコンポーネントを埋め込む。カーネルは不要な機能が無効化されたオプションを使用して crosstool-ng で最小限の 32 ビット RISC-V イメージとしてコンパイルされ、イニトラムフスには静的にリンクされた「Hello World」イニシャルプログラムおよび/dev/kmsg などの必須デバイスが含まれる。DTS ファイルから生成された DTB は初期コンソール出力を構成し、イニシャルプロセスの場所を指定する。

重要なのは、エミュレーター全体を WebAssembly にクロスコンパイル可能なことであり、これにより複雑なビルド環境や新しい PCB デザインなしに Linux ディストリビューションをブラウザ上で直接実行できる。生産用途には最適化されていない(コールドブートには最大 1 分かかる可能性がある)が、この軽量ツールは組み込み Linux 開発への参入障壁を下げ、RISC-V アーキテクチャの学習のためのアクセス可能なプラットフォームを提供する。

本文

Linux の稼働と RISC-V エミュレーター構築ガイド

概要

近年、組み込みシステムにおいてLinuxが主流のオペレーティングシステムとなっています。その魅力は柔軟性だけでなく、多数の開発者によるドライバ実装にあります。しかし、カスタムハードウェアでは当初、カーネルを自ら起動させる(ブリングアップ)作業が必要です。

本稿では、新規プラットフォームでの Linux 稼働に必要な最小限の設定手順について解説します。対象は新設計基板ではなく、RISC-V CPU のエミュレーターです。

Linux 稼働の要件

複雑なソフトウェアである Linux を動かすために必要なハードウェア要件は以下の 3 つだけ suffices です。

  • **MMU(メモリ管理ユニット)**付きの CPU
  • RAM: カーネル、デバイスツリー、initramfs を読み込む十分な容量
  • システムクロックタイマー: 定期的な割り込み発生機能

エミュレーターの作成

上記要件を実装するため、約 2,000 行の C++ コードで以下の実装を行いました。

  • CPU: RV32IMA インストラクションセットの実装(MMU 機能含む)
  • RAM: シングル RAM パフォーマンスの実装
  • タイマー: SBI(Supervisor Binary Interface)インタフェース
  • UART (シリアル通信): WD8250 チップベースの実装

完全な実装コードは以下で入手可能です。

WerWolv/riscv-emulator

実際のハードウェアとの違い

本稿では自作パーフェリカルを使用しますが、実際の SoC ではデータシートを確認してマッピングアドレスを特定し、ブートローダー(u-boot など)を用いて不揮発性ストレージから起動します。

エミュレーターでの最終的なハードウェア定義例は以下の通りです。

struct Hardware {
    Hardware() {
        // パフォーマンスのアドレス空間にマッピング
        cpu.address_space().map(0x0000'0000, &ram);
        cpu.address_space().map(0xF400'0000, &uart8250);

        // RISC-V 互換の MMU をアドレス変換器として追加
        cpu.address_space().add_address_translator(&riscv_mmu);

        // UART パフォーマンスの設定:出力をターミナルに印刷
        uart8250.output_callback([](std::uint8_t c) {
            putchar(c);
            fflush(stdout);
        });

        // カーネルを RAM の先頭に読み込む
        ram.write(0x00, LinuxKernel);

        // デバイストゥリーを RAM の末尾に読み込む
        constexpr static auto DeviceTreeBlobLoadAddress = 512_MiB - 1_MiB;
        ram.write(DeviceTreeBlobLoadAddress, DeviceTreeBlob);

        // ブートローダーと同様に、デバイスツリーのアドレスをレジスタ a1 に設定
        emulator.cores()[0].a1() = DeviceTreeBlobLoadAddress;

        // 固定アドレスに initramfs を読み込み(後で参照可能)
        constexpr static auto InitRamFsLoadAddress = 0x1F700000;
        ram.write(InitRamFsLoadAddress, InitRamFs);

        // CPU の起動
        cpu.power_up();
    }

    auto step() -> void {
        cpu.step();
    }
private:
    riscv::Cpu<1> cpu;
    dev::riscv::MMU<std::uint32_t> riscv_mmu;
    dev::Ram ram(512_MiB);
    dev::UART8250 uart8250;
}

ツールチェーンの構築

Linux を動かすには、まずプラットフォーム向けにカーネルを構成・コンパイルする必要があります。手作業での環境構築は複雑であるため、crosstool-ng プロジェクトを利用し、以下のタスクを自動化します。

  • コンパイラとライブラリの入手
  • 構成とコンパイルの実行

ターゲットの選択

32 ビット RISC-V Linux を動作させるため、

riscv32-unknown-linux-gnu
のツールチェーンが必要です。これにより GCC コンパイラと glibc が提供されます。

$ ./bootstrap
INFO  :: *** Generating package version descriptions...
INFO  :: *** Done!
$ make
gmake[1]: Entering directory 'crosstool-ng'...
GEN      ct-ng
$ ./ct-ng riscv32-unknown-elf
...
Now configured for "riscv32-unknown-elf"
$ ./ct-ng menuconfig

menuconfig
を実行して設定メニューを開き、以下の項目を変更します。

  • Target options: 「Use the MMU」を有効化
  • Operating System: 「Target OS」をLinuxに設定
  • C library: 「C library」をglibcに設定
  • Enable C compiler: C++ コンパイラーを有効化

設定を保存し、

./ct-ng build
を実行します。完了すると
~/x-tools/riscv32-unknown-linux-gnu
にツールチェーンがインストールされます。

Linux カーネルのコンパイル

ツールチェーン準備ができたら、Linux カーネル(torvalds/linux)をコンパイルします。デフォルトの RISC-V 設定をベースにし、以下の点を変更します。

  • Platform type: 「Allow configurations that result in non-portable kernels」を有効化
  • Base ISA: RV32I に設定(64 ビットから 32 ビットへ)
  • 拡張機能サポート: エミュレーター未実装のため、すべてを無効化
  • Boot options: 「UEFI runtime support」を無効化
    • ※エミュレーターは圧縮命令セットをサポートしていないため、「Emit compressed instructions」も無効化します。

以下の設定も行います。

  • Target options:
    • 「Generate code for the specific ABI」を
      ilp32
      に設定
    • 「Architecture Level」を
      rv32ima
      に設定
  • Kernel Features:
    • タイマー用の SBI v0.1 サポートを有効化
    • リロケタブルカーネル(relocatable kernel)構築を有効化
    • 肥大化要因である Virtualization, Loadable module support, Networking support, Cryptographic API を無効化

設定完了後、以下のコマンドでコンパイルを開始します。

$ make -j \
    ARCH=riscv \
    CROSS_COMPILE=/path/to/riscv32-unknown-linux-gnu-

make への回答はすべて

n
で構いません。コンパイル完了後、以下に最終実行ファイルが存在します。

  • ファイル場所:
    arch/riscv/boot/Image

init プログラムのコンパイル

ユーザー空間到達直後に動作するシンプルな init プログラムを作成します。実際の組み込みでは busybox が一般的ですが、ここではテキスト出力用の簡易 C コードを使用します。

#include <stdlib.h>
#include <stdint.h>
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>

int main() {
  // ログを直接カーネルログへ出力するため /dev/kmsg を開く
  int kmsg = open("/dev/kmsg", O_WRONLY);
  if (kmsg == -1) {
    return EXIT_FAILURE;
  }
  // メッセージの印刷
  dprintf(kmsg, "Hello World from \033[32mLinux Userspace\033[0m!\n");
  // init プロセスが終了しないように無限ループ
  while (true) {
    sleep(1);
  }
  // ファイルハンドルを閉じる
  close(kmsg);
  return EXIT_SUCCESS;
}

共有ライブラリ依存を避け単独で動作するため、静的リンク(statically linked)します。

$ riscv32-unknown-linux-gnu-gcc -static init.c -o init

initramfs の作成

initramfs はシステム起動後に利用可能なファイルシステム(cpio アーカイブ)です。ルート

/
にマウントされ、通常
/bin
,
/dev
,
/proc
などを含みます。

再現性のある構成を生成するために、以下のテキストファイルを

gen_init_cpio
ツールで処理します。

initramfs 定義ファイル (

initramfs.txt
)

# /bindir /bin 755 0 0file /bin/init init 755 0 0
# /devdir /dev 755 0 0nod /dev/kmsg 644 0 0 c 1 11nod /dev/initrd 644 0 0 b 1 250nod /dev/console 600 0 0 c 5 1nod /dev/null 666 0 0 c 1 3nod /dev/ram 644 0 0 b 1 0nod /dev/root 644 0 0 b 4 0nod /dev/ttyS0 660 0 0 c 4 64
# /procdir /proc 755 0 0

アーカイブを生成します。

$ gen_init_cpio initramfs.txt -i initramfs.cpio

デバイスツリーの作成

Linux カーネルはデバイスツリー(Device Tree)に基づいてドライバのロードや設定を行います。以下の DTS ファイルで最小限のハードウェアを定義します。

riscv-emulator.dts

/dts-v1/;
/ {
  #address-cells = <0x01>;
  #size-cells = <0x01>;
  model = "WerWolv's Emulator";

  cpus {
    #address-cells = <0x01>;
    #size-cells = <0x00>;
    timebase-frequency = <65000000>;

    cpu@0 {
      device_type = "cpu";
      compatible = "riscv";
      reg = <0x00>;
      riscv,isa = "rv32ima";
      mmu-type = "riscv,sv32";

      interrupt_controller: interrupt-controller {
        #interrupt-cells = <0x01>;
        #address-cells = <0x00>;
        compatible = "riscv,cpu-intc";
        interrupt-controller;
      };
    };
  };

  memory@0 {
    device_type = "memory";
    reg = <0x00 (512 * 1024 * 1024)>;
  };
}

パーフェリカルは

soc
ノード下に配置します。

/ {
  // ...
  soc@F0000000 {
    #address-cells = <0x01>;
    #size-cells = <0x01>;
    compatible = "simple-bus";
    ranges = <0x00 0xF0000000 0x10000000>;

    serial@4000000 {
      compatible = "ns8250", "ns16550";
      reg = <0x4000000 0x100000>;
      interrupt-parent = <&interrupt_controller>;
      interrupts = <0x01>;
      no-loopback-test;
      clock-frequency = <5000000>;
    };
  };
};

カーネル設定(

chosen
ノード)を以下のように記述します。

/ {
  // ...
  aliases {
    serial0 = "/soc@F0000000/serial@4000000";
  };

  chosen {
    bootargs = "earlycon console=/dev/ttyS0 rdinit=/bin/init root=/dev/initrd";
    stdout-path = "serial0";
    linux,initrd-start = <0x1F700000>;
    linux,initrd-end = <0x1FEFFFFF>;
  };
};

重要なのは

chosen
ノード内の値です。

  • bootargs: earlycon で早期ログ出力を有効化し、UART(serial0)に割り当てます。また、ユーザー空間到達直後には
    /bin/init
    を起動させます。
  • linux,initrd-start/end: initramfs のメモリアドレスを定義します。

DTS ファイルを DTB(Device Tree Blob)に変換します。

$ dtc -I dts -O dtb -o riscv-emulator.dtb riscv-emulator.dts

すべてを組み合わせて

作成した 3 つのファイルをエミュレーターに埋め込み、実行します。

constexpr std::uint8_t LinuxKernel[] = {    #embed "Image"};
constexpr std::uint8_t DeviceTreeBlob[] = {    #embed "device_tree.dtb"};
constexpr std::uint8_t InitRamFs[] = {    #embed "initramfs.cpio"};

実行結果は以下の通りです。

$ ./emulator
[    0.000000] Booting Linux on hartid 0
[    0.000000] Linux version 7.0.0-00166-g0f0013213293 (werwolv@fedora) ...
[    0.000000] Machine model: WerWolv's Emulator...
[   54.748992] Run /bin/init as init process
[   54.814251] Hello World from Linux Userspace!

ブラウザでの実行

作成したバイナリは WebAssembly(Wasm)へクロスコンパイル可能で、JavaScript から呼び出せるためブラウザ上で動作させることもできます。ただし、起動まで約 1 分かかる場合があり、これはエミュレータが最適化されていない環境下であることを示しています。

まとめ

以上により、RISC-V エミュレーター上で Linux システムを構築することができました。

  • プロセス自体は実際のハードウェアでも同様ですが、メモリーマッピングは SoC データシートに基づき行う必要があります。
  • 実際の組み込みシステムではブートローダー(u-boot など)がカーネルや initramfs を配置し、独自のファイル形式でバンドリングします(Android ブートローダーマニュアル参照)。

使用したコードとエミュレーターは GitHub で公開しています:

WerWolv/riscv-emulator

同じ日のほかのニュース

一覧に戻る →

2026/09/08 3:52

ロサンゼルスが一つ一つのビル建設を通じて発展していく姿を見てください(1880 年~2026 年)

## Japanese Translation: 本テキストは、ロサンゼルス独自の視覚マップを説明している。既存の構造物それぞれが建設年によって配置された一つの箱として表現されており、現在も存続している建物のみが含まれ、取り壊されたものは除外されている。その結果、この可視化は都市の歴史的景観や完全なタイムラインではなく、現在の建築環境を示している。これは生存する建築への明確な年代的概要を提供する一方で、過去の構造物や将来の開発については洞察を与えず、ロサンゼルの進化を現在にのみ焦点を当てており不完全な画像として提示する。 ## Text to translate : This text describes a unique visual map of Los Angeles where each existing structure is shown as one box arranged by its construction year. Only buildings still standing today are included; those that have been demolished are excluded. As a result, the visualization shows the current built environment rather than the city's historical appearance or full timeline. While it provides a clear chronological overview of surviving architecture, it offers no insight into former structures or future developments, presenting an incomplete picture of Los Angeles's evolution by focusing solely on the present.

2026/09/05 20:25

Linux ディストリビューション全体に対する信頼を誤用した攻撃 (Trust-Trust Attack)

## Japanese Translation: Ken Thompson の「信頼の連鎖」攻撃は、以前はコンパイラレベルでの改ざんが必要とされていたものが、GNU strip 内でも実行可能であることを実証した。本研究では、GNU strip が完成済みの ELF ファイルを改変することで、ソースコードを確認せず・改変せずにも、Linux 全体のビルド環境に永続的なバックドアを埋め込むことを示している。NixOS のブートストラッププロセスにおいて、バイナリシードに含まれる GNU strip の単一のパッチ済みバージョンによってペイロードが挿入され、それが次のビルド世代へと伝播し、シードが依存関係の閉じ外に出た後の最終的な標準環境においても存続する。実際の nixpkgs リビジョンにおいて、この攻撃は障害なく完全なグラフィカルインストールメディアを構築でき、任意の悪意のある挙動を行う汚染されたバイナリを生み出した。この発見は、NixOS のような不変系ディストリビューションに依存するユーザーや組織に対し、一見安全なツールが大規模な侵害の主要なベクトルとなるリスクを提起している。この研究成果は Aman Sharma(メール:[メールを表示])によって提出され、UTC 2026 年 7 月 27 日 12:59:15 に投稿された(バージョン 1)。

2026/09/04 1:06

WeatherNext 3

## 日本語翻訳: ## サマリー:Google は、年間毎日ごとに正確な時間ごとの予報を生成する、これまで最も高度な全球気象 AI モデル「WeatherNext 3」をローンチしました。以前のモデルとは異なり、本モデルは生きた衛星画像を直接分析することで、降雨や降雪といった急速な変化を追跡し、トレーニングが施されたことのない場所であっても精密なローカルデータを提供します。この機能は、ジャマイカでのハリケーンメリサの上陸地点を特定して予測することを支援するなど、最近のイベントにおいて極めて重要であることを実証しました。Google Search、Maps、Gemini などの日常的な消費者向け利用に加え、WeatherNext 3 は風力発電所や太陽光発電所向けの放射線量および雲量予測という重要な産業用途に最適化されています。これにより、運営者は運用コストを削減しながら再生可能エネルギー源をより効率的に管理できるようになります。また、温度などの地表変数を 5km〜10km の高精度で予測することで、インフラがより信頼性高く動作するよう確保するとともに、ユーザーには日々の意思決定を賢く行うための精密なローカルデータを提供します。

Linux カーネルを新しいプラットフォームでビルドする手順 | そっか~ニュース