2026年2月8日日曜日

Rust@Hello x86!!

Rust@Hello x86!!

OS自作をRustに切り替えるかはまだ模索中だけれど、まずはx86-pc上で「Hello x86!!」を表示するカーネルこと始めをしてみた。

やりたいこと

  • OSとカーネルはGitリポジトリを別にする
  • IBM PC/AT互換機上(QEMU)で動作させる
  • ブートにGNU GRUBを使用する
  • カーネルのファイル形式はELFを使用する

環境を作る

nightly版rustをインストールしておく

参考:付録G: Rustの作られ方と“Nightly Rust”

$ rustup install nightly

ディレクトリを作る

MochiOSというプロジェクトディレクトリ配下に作っていく。
とりあえず、OSで使用するカーネルは別プロジェクトとしたいので、depsディレクトリ内にkernelプロジェクトcargoで作る。
また、kernelはnightly版rustを使用する前提にしておく。

$ mkdir MochiOS
$ cd MochiOS
$ git init
$ mkdir deps
$ cd deps
$ touch .gitkeep
$ cargo new kernel
$ cd kernel
$ rustup override set nightly

OSプロジェクトではdeps配下のディレクトリを管理したくないので、.gitignore、deps/.gitkeepを作成しておく。

# .gitignore

/deps/*

今は作成しないけど、deps配下の各サブプロジェクトはクローンしてくるシェルスクリプトを作る予定。

「Hello x86!!」を表示するコードを書く

標準ライブラリの無効化、Panic処理の実装、VGAのテキストバッファに文字列「Hello x86!!」の書込みを行って無限ループ。

// MochiOS/deps/kernel/src/main.rs

#![no_std]
#![no_main]

use core::panic::PanicInfo;

const VGA_BUFFER: *mut u8 = 0xB8000 as *mut u8;

#[panic_handler]
fn panic( _info: &PanicInfo ) -> !
{
    loop {}
}

#[unsafe( no_mangle )]
pub extern "C" fn _start() -> !
{
    let str = b"Hello x86!!";

    for ( idx, &byte ) in str.iter().enumerate() {

        unsafe {
            *VGA_BUFFER.add( idx * 2 )     = byte;
            *VGA_BUFFER.add( idx * 2 + 1 ) = 0x0F;
        }

    }

    loop {}
}

multibootヘッダを書く

これ書いとけばGRUBがカーネルをロードしてくれるってよ。(自前でローダ用意しなくていいから楽だなぁ)

// MochiOS/deps/kernel/src/multiboot.rs

#[repr( C ) ]
pub struct MultibootHeader {
    magic   : u32,
    flags   : u32,
    checksum: i32,
}

#[unsafe( link_section = ".multiboot" )]
#[unsafe( no_mangle )]
pub static _MULTIBOOT_HEADER: MultibootHeader = MultibootHeader {
    magic   : 0x1BADB002,
    flags   : 0,
    checksum: -( 0x1BADB002 as i32 ),
};

ファイル追加したのでmain.rsにモジュール宣言を追記。

// MochiOS/deps/kernel/src/main.rs

module multiboot

コンパイルする

ターゲットファイルを作る

MochiOS/deps/kernel/src/x86_32.json(JSONってコメント書けないからパス表示を欄外に・・・)

{
    "llvm-target"         : "i686-unknown-none",
    "data-layout"         : "e-m:e-p:32:32-p270:32:32-p271:32:32-p272:64:64-i128:128-f64:32:64-f80:32-n8:16:32-S128",
    "target-pointer-width": 32,
    "arch"                : "x86",
    "vendor"              : "unknown",
    "os"                  : "none",
    "linker"              : "rust-lld",
    "linker-flavor"       : "ld.lld",
    "disable-redzone"     : true,
    "panic-strategy"      : "abort"
}

リンカスクリプトを書く

// MochiOS/deps/kernel/src/x86_32.lds

ENTRY( _start )

MEMORY {
    KERNEL : ORIGIN = 0x00100000, LENGTH = 0x100000
}

SECTIONS {
    .multiboot : {
        KEEP( *( .multiboot ) )
    } > KERNEL

    .text : {
        *( .text* )
    } > KERNEL

    .rodata : {
        *( .rodata* )
    } > KERNEL

    .data : {
        *( .data* )
    } > KERNEL

    .bss : {
        *( .bss* )
        *( COMMON )
    } > KERNEL
}

cargoの設定を書く

# MochiOS/deps/kernel/.cargo/config.toml

[unstable]
build-std        = [ "core" ]
json-target-spec = true

[build]
target = "./src/x86_32.json"

[target.'cfg( all( target_arch = "x86" ) )']
rustflags = [ "-C", "link-arg=-Tsrc/x86_32.lds" ]

実行する

イメージファイルのディレクトリを作る

$ cd MochiOS
$ mkdir -p iso/boot/grub

カーネルを置く

$ copy deps/kernel/target/x86_32/debug/kernel iso/boot/

grub.confを作る

# MochiOS/iso/boot/grub/grub.cfg

set timeout=0
set default=0

menuentry "MochiOS" {
    multiboot /boot/kernel
    boot
}

イメージファイルを作る

$ grub-mkrescue -o mochios.iso iso/

QEMUで実行する

デスクトップ環境でなはないので、VNCサーバを立ち上げて別PCからリモート接続。

$ qemu-system-i386 -cdrom mochios.iso -vnc :0

enter image description here
できた。

手こずりpoints

ldスクリプトが反映されない

試行錯誤中にreadelf -a kernelして確認すると.multibootセクションが作られていなくて。
原因は、cargo君がファイル依存関係でldスクリプトを知らないので、更新されてもコンパイルし直しが発生しなかった。
なので、下記コマンドで対応。

$ cargo clean
$ cargo build

(っていうかtargetディレクトリの容量がえげつないなrust・・・coreとかbinutlもクロス先用にコンパイルしているからなんだろうけど)

流石に、手で毎回clean打つのはめんどくさいので、ldスクリプトファイルをcargo君に監視してもらうようにした。

# MochiOS/deps/kernel/Cargo.toml

[package]
build = "build.rs"
# MochiOS/deps/kernel/build.rs

fn main()
{
    println!( "cargo::rerun-if-changed=./src/x86_32.lds" );
}

grub-rescue実行してもISOファイルが生成されない

下記インストールで対応

$ sudo apt install grub-pc-bin xorriso
  • grub-pc-binは、「DebianやUbuntuなどのLinux環境において、BIOSベースのPCでGRUB 2ブートローダを使用するために必要な、GRUBのコアイメージ(モジュール)群を収録したバイナリパッケージ」だそう。
  • xorriso(えぐぞりっそ)は、「ISO 9660(Rock Ridge拡張対応)イメージの作成、操作、およびCD/DVD/BDメディアへの書き込みをコマンドラインで行う多機能ツール」だそう。

2026年1月5日月曜日

Rust@はじめてみた(インストール)

Rust@はじめてみた(インストール)

仕事が落ち着いて、年も明けたので、新しいことを始めたい。
そういえば、Rustという言語があるということは知っていたけれど、何も知らないなと思ってちょっとやってみるか!と。C言語大好き人間だけど、Rustにその座が取れるだろうか。

Rustとは

何はともあれ、ホームページ( https://rust-lang.org/ja/ )にいくと3つのキーワードが挙げられていた。

  • パフォーマンス
    ガベージコレクタが無くメモリ効率が良く非常に高速らしい。組込み機器上で動作させることも念頭に置かれているようだ。
  • 信頼性
    なんとなく知っていたのはこっちのほうで、コンパイル時に様々な種類のバグを見つけられるらしい。ベアメタルプログラミングとしてここらの機能がどれだけ利用できるのか期待。信頼性という言葉、好き。
  • 生産性
    コンパイラ君のエラーメッセージが丁寧らしい。最近のgccもすごい親切だからな・・・。とはいえ、ツール類が色々揃っているらしいというところは手を付けるに当たって安心感がある。

Rustの活用事例としては、FireFox、Dropboxなどなどあるらしい。最近LinuxやWindowsもRustに置き換えていくみたいな話があった様な(むしろこの話題で勉強する気になった)

インストール

さすがに日本語ドキュメントも豊富で、こちら( https://doc.rust-jp.rs/book-ja/ch01-01-installation.html )を参照しながらUbuntuにインストールする。

  • rustup
    Rustのバージョンと関連するツールを管理するコマンドラインツールだそう。

まず、これを打て。

curl --proto '=https' --tlsv1.2 https://sh.rustup.rs -sSf | sh

色々説明されて、最後に入力を求められた。

1) Proceed with standard installation (default - just press enter)
2) Customize installation
3) Cancel installation
>

ドキュメントには無いが、1を選択しておく。

Rust is installed now. Great!

To get started you may need to restart your current shell.

無事インストールされたようだ。
環境変数も弄ってくれている様なので反映してから、とりあえずバージョン表示。

$ source ~/.bashrc
$ rustc --version
rustc 1.92.0 (ded5c06cf 2025-12-08)

アップデート

下記コマンドで簡単にできるらしい。忘れそう。

$ rustup update

アンインストール

下記コマンドで簡単にできるらしい。忘れてもいいか。

$ rustup self uninstall

2024年9月23日月曜日

git@よく使うコマンド早見表

git@よく使うコマンド早見表

gitでよく使うコマンドをまとめる。

add

コマンド 説明
git add . ワークディレクトリ配下全てのファイルをステージ(インデックスに記録)する
git add <ファイル> 指定したファイルをステージ(インデックスに記録)する
git add -u ワークツリー内でバージョン管理中の全てのファイルをステージ(インデックスに記録)する

branch

コマンド 説明
git branch ローカルブランチのリストを表示する
git branch -r リモートブランチのリストを表示する
git branch -a ローカルブランチとリモートブランチのリストを表示する
git branch <ブランチ名> 指定したブランチを作成する
git branch -d <ブランチ名> 指定したブランチを削除する

checkout

コマンド 説明
git checkout <ブランチ名> 指定したブランチに移動する
git checkout -b <ブランチ名> 新しくブランチを作成してそのブランチに移動する

clone

コマンド 説明
git clone <リポジトリアドレス> リポジトリアドレスからクローンする
git clone --recursive <リポジトリアドレス> リポジトリ内のサブモジュールもクローンする

commit

コマンド 説明
git commit コミットする(コミットログを編集するエディタが起動する)
git commit -m <コミットログ> 指定したコミットログでコミットする
git commit -a git add -uとgit commitを連続して実行する

config

コマンド 説明
git config --list 設定を確認する
git config --global core.editor vim エディタをvimに設定する
git config --global diff.tool vimdiff diffをvimで見るように設定する

diff

コマンド 説明
git diff ワークツリーとインデックス間のファイル差分を表示する
git diff HEAD ワークツリーと最新のコミット間のファイル差分を表示する
git diff – cached インデックスと最新コミット間のファイル差分を表示する

fetch

コマンド 説明
git fetch リモートリポジトリが更新されているか調べる

log

コマンド 説明
git log コミットログを表示する
git log -p コミットログとコミット毎の差分をパッチ形式で出力する
git log -<ログ数> コミットログを指定した数だけ表示する
git log --pretty=short 各コミットログの最初の1行(要約)だけを表示する

merge

コマンド 説明
git merge <ブランチ名> 現在のブランチに指定したブランチをマージする

pull

コマンド 説明
git pull 現在のブランチの最新コミットとリモートブランチの最新コミットをマージしてコミットする

push

コマンド 説明
git push <登録リポジトリ名> <ブランチ> 登録リポジトリのブランチにプッシュする

status

コマンド 説明
git status 現在の状態を表示する

submodule

コマンド 説明
git submodule init ローカルの設定ファイルを初期化する
git submodule update [<サブモジュールディレクトリ>] 登録されているサブモジュールをチェックアウトする
git submodule update --remote [<サブモジュールディレクトリ>] 登録されているサブモジュールをリモートリポジトリの内容にマージする(サブモジュールディレクトリ内でgit fetch;git mergeの実行と同等)
git submodule add -b <ブランチ> <リポジトリアドレス> 指定したリポジトリと同じディレクトリ名で指定したブランチのサブプロジェクトのデータを格納する

2024年7月21日日曜日

MochiOS開発記@突然のTripleFaultの原因判明!

 デバッグログ強化を進めたお陰でタイトルの通り原因判明に至った。

以下、なりゆき。


(1) デバッグログをメモリ上にも吐き出すように機能追加

(2) TripleFaultを再現させるも、吐き出したメモリ上のログが破壊されていることに気づく

(3) GDB繋いでログメモリ先頭アドレスにwatchpoint設定して監視するも、引っ掛からない

 ⇒つまり、別の仮想アドレスからメモリ破壊していることに気づく

(4) ページマッピング箇所にトレースログを仕込み

(5) どんどんページに割り当ててる物理アドレスが加算されていることに気づく

 ⇒つまり、メモリリーク

(6) 怪しい所にどんどんトレースログ追加していき原因判明



直接的原因1:

 プロセスのヒープ領域を縮小する時に、ページマッピング解除は行っているが、割り当てている物理メモリを解放していなかった。

直接的原因2:

 普通に設計上固定的に使っている物理メモリ領域をカーネルの物理メモリ管理くんが未使用と認識していて、割当てを行っていた。


直接的原因1の動機的原因(根本)は、後で実装すればいいや精神でテキトーに実装したからだな・・・。ページマッピング解除時に関数のインタフェース仕様が理由で割り当てていた物理メモリアドレスがわからないから解放ができず、割と大がかりな構成変更が必要だったから後回しにしていたようだ。なんにも残してない自分が悪いね。個人開発でもTODO管理やチケット管理ってやっぱ必要だと思う。うん。


直接的原因2のほうは・・・なんでだろうね。どう考えていたか覚えていない。



あー超スッキリした!メモリマネージャ大改造しよ。これが当面の目標。


2024年6月1日土曜日

MochiOS開発記@デバッグログ強化検討

 MochiKernelのデバッグログは下記情報を持っている。

・ログ出力したモジュール-サブモジュール(つまりひとつのソースファイル)のID

・ログ出力した行数

・文字列


デバッグログ種別やレベルなどは持たせていなくて、好き勝手していた。

いざ不具合があると情報が足りなくなるし、不具合がないと不要なログで埋め尽くされて見たいものが見れなくなる、ということに悩まされてきたので、種別の概念を追加することにする。


大体、世の中のシステムログ種別は以下のような感じか(超適当)

種別 概要
emergency システムが使用できなくなった系
alert 対処しないとまずい系
critical 致命的で対処しようがない系
error エラー全般
warning 警告全般
notice エラーでも警告でもないがちょっとあやしい系
informational なんか処理したよ系
debug プログラムデバッグ情報系

参考にしてこんな感じにしよう。(全然参考にしてない)
種別 概要
abort あぼーと。もう何もできない。死んだ。
error 想定外のエラー。処理を続けられないので中断。
warning 想定内のエラー。処理を続けられないので中断。
info なんか処理した。頻度低(HW割込みとかタスクスイッチ系はダメ)。
trace コードここ通ったよ。頻度低(HW割込みとかタスクスイッチ系はダメ)。
temp デバッグ中に使う専用。残さない。

種別ごとに有効化マクロ定義しとこ。

2024年5月28日火曜日

MochiOS開発記@当面の目標

突然のフリーズ。 カーネルログを見ても解析困難。
というわけで、このバグ修正を当面の目標にしよう。
INT14なのでページフォルトなのだけど、誰かがメモリ破壊してる気がするなぁ。。。
とりあえず、カーネルログのレベル実装してもうちょい情報増やしてみよう。

2024年3月3日日曜日

CUnit@単体テストはじめ

CUnit@単体テストはじめ

はじめに

自作kernel作るにあたってDebug機能が欲しくなり。
snprintfが必要となり。
やっぱlibc欲しいなとlibcを作り始め(newlib使ってもよかったんだけど、自作楽しいので)。
snprintfを作ったのだが、これ動くのか?と。
単純に作業量2倍になるので、趣味でテスト系は避けてきたのだけど、やっぱ避けて通れないよねテスト。
というわけで、CUnit使うか・・・の備忘録。

ライブラリインストール

sudo apt-get install libcunit1-dev

テスト対象コード例(target.c)

int target_func( void )
{
	return 5;
}

テストコード例

#include <CUnit/CUnit.h>
#include <CUnit/Basic.h>

#include "target.c"

void test_func_1( void )
{
	ret = target_func();

	CU_ASSERT_EQUAL( ret, 5 );
}

int main( void )
{
	CU_pSuite suite;

	/* テストレジストリ初期化 */
	CU_initialize_registry();
	
	/* テストスイート追加 */
	suite = CU_add_suite( "TestSuite", NULL, NULL );

	/* テスト追加 */
	CU_add_test( suite, "test1", test_func_1 );

	/* テスト実行 */
	CU_basic_set_mode( CU_BRM_VERBOSE );
	CU_basic_run_tests();

	/* テストレジストリ削除 */
	CU_cleanum_registry();

	return 0;
}

static関数をテストしたい場合も考慮して、テスト対象コードをinclude。

サブ関数をstub化

サブ関数をstub化する方法はいくつかあるが、テスト対象コードをいっさい変えずにやるのは無理っぽい。
下記が最善策かなぁ。検索してもいい案が出てこないので自分で考えてみた。

#ifndef CALL
#define CALL( a ) a
#endif

#ifndef PROTO
#define PROTO( a, b ) a b
#endif

PROTO( extern int, target_sub( void ) );

int target_sub( void )
{
	return 5;
}

int target_func( void )
{
	return CALL( target_sub() );
}

テストコードは、テスト対象コードのincludeの前に下記を定義しておく

#define CALL( a ) stub_##a
#define PROTO( a, b ) a stub_##b

テスト対象コードincludeの後に下記を定義する

int stub_target_sub( void )
{
	return 5;
}

Rust@Hello x86!!

Rust@Hello x86!! OS自作をRustに切り替えるかはまだ模索中だけれど、まずはx86-pc上で「Hello x86!!」を表示するカーネルこと始めをしてみた。 やりたいこと OSとカーネルはGitリポジトリを別にする...