simotin13's message

simotin13といいます。記事の内容でご質問やご意見がありましたらお気軽にコメントしてください\^o^/

pcduinoでOSを自作する ~第1回 環境構築~

最近OSの仕組みを勉強しているのですが、教科書的な勉強だけだとなんだか退屈してしまうのでARMの基板でOS自作してみることにしました。
この手の趣味プロジェクトはなかなか長続きしないことが多いのですが、モチベーション維持のためにも活動記録をあげておきたいと思います。

ターゲット

ターゲットにするのはpcDuinoという基板です。
f:id:simotin13:20190414214420j:plain


pcDuino Linux Dev Board
akizukidenshi.com

私の手元にはこの基板があるのですが、V2という新しいバージョンの基板も出ているようです。*1
akizukidenshi.com

なぜこの基板にしたのか?

Coretex-A8はA系のCPUとしては古いCPUになりますが、あえてこのCPUで試してみたいと思います。
理由はコア数が1だからという単純な理由です。RaspberryPiのような最近の基板では、CPUが64bitだったり複数コアだったりします。
今回のOS自作は自分の勉強・教育が目的なのでできれば機能が少ない方がやりやすいかな考えています。*2

目標

ざっくりとはARMの基板でOSを作るということですが、もう少し具体的なゴールを決めておきたいと思います。

MMUを使うこと

組み込みの仕事をしているのでMMUのないマイコンを扱うことが多いです。あるいはCortex-A系のマイコンを使う場合はBSPのLinuxが載ること多いので、MMUを扱うOSのことが今だによく分かっていません。

これを機会にMMUの扱い・仮想メモリ・ページングといった機能についてきちんと理解することを目標にしたいと思います。

Ethnernetコントローラを動かす

せっかくOSを書くのであれば最低限Ethernetは対応したいです。
でもプロトコルスタックどうしよう。まぁそれはそれでネットワークの勉強にもなって一石二鳥になるはず。

HDMIGUI出力をすること

HDMIのコントローラとか触ったことないですが、夢は大きく持ちたいと思います。*3

環境構築

OSを開発するにあたって準備が必要です。

用意するもの(ハードウェア)

  • pcDuino

秋月電子から購入しました。

  • usb miniBケーブル

電源用です。

  • USB-シリアル変換ケーブル

OS自作する場合、最初はパソコンのような「画面」が存在しません。動作確認はもっぱらLEDやUARTを使って行います。
パソコンからUARTの出力を確認するためにUSB-シリアル変換ケーブルが必要になります。
この手のケーブルは秋月電子やアマゾンで購入できます。合わせてオス-メスのジャンパワイヤも必要になります。

  • micro SDカード

自作したOSはmicro SDカードに書き込んで起動させます。
*4

開発するPCを除くと、物理的に必要なものは上記4点になります。
開発作業はLinux環境で行います。開発用のツールチェインがインストールでき、microSDカードを認識できればなんでも構いません。
私はMintLinux 18.3を使用して開発をしていきます。ツールチェインのインストールはXxxで説明します。

開発環境の構築

開発環境の構築としては、

  1. クロス開発用のツールチェインのインストール
  2. AllWinner用のイメージ作成ツールのビルド

の2ステップが必要です。

クロス開発用のツールチェインのインストール


sudo apt install gcc-arm-none-eabi
で完了です。
/usr/bin/ 以下に arm-none-eabi-*** のツールチェイン一式がインストールされます。

AllWinner用のイメージ作成ツールのビルド

自作したOSはSDカードに書き込んでブートするのですが、単純にプログラムが書き込んであるだけでは駄目で、AllWinner用のブートイメージに変換する必要があります。イメージのロードをBROM内のプログラムがロードしてくれるのですが、チェックサムなどのチェックを行っているようです。

変換ツールはu-bootのビルドツールの1つとして公開されているようですが、手っ取り早くビルドできるよう
こちらの方が修正されたコードを公開されていますので利用させて頂きした。
github.com


git clone https://github.com/hipboi/mksunxiboot.git
cd mksunxiboot
make
sudo cp mksunxiboot /usr/local/bin

参考にするレポジトリ

これでBaremetalからのOS自作の準備は整いましたが、ブートやUARTの実装など、こちらの実装を参考にさせて頂きます。

github.com

つづき

環境構築はできたので、さっそく開発に着手しますがこの記事はここまでにしておきます。

次の記事では、CPUのリセット後の状態、UARTの有効化、Lチカ等について行った成果を挙げたいと思います。

*1:CPUは同じCoretex-A8のようです

*2:それでもワンチップマイコンからすると多機能なので途中で挫折する可能性が高いですが

*3:コントローラのレジスタ数多そうだな

*4:容量は4GBや8GBの少ないサイズでも問題無いと思います

Web+DB vol.109 を読んだ

Ruby2.6の記事が気になって買ってみたのですが、面白い記事があったのでとりあえず自分のためにメモを残しておきたいと思います。
最近は雑誌を買っても一度も開かずに積本になることもあるのでとりあえずこういったメモをアウトプットするだけでも、後々振り返ったりする際に道しるべになりそうなので質と量はともかく書いておきます。

WEB+DB PRESS Vol.109

WEB+DB PRESS Vol.109

  • 作者: 佐藤歩,加藤賢一,原一成,加藤圭佑,大塚健司,磯部有司,村田賢太,末永恭正,久保田祐史,吉川竜太,牧大輔,ytnobody(わいとん),前田雅央,浜田真成,竹馬光太郎,池田拓司,はまちや2,竹原,原田裕介,西立野翔磨,田中孝明
  • 出版社/メーカー: 技術評論社
  • 発売日: 2019/02/23
  • メディア: 単行本
  • この商品を含むブログを見る

Ruby 2.6徹底解剖

@mrkn さんの記事。

Rubyのリリース予定について

Ruby3.0が2020年12月25日のリリースを目標としているなんて知らなかった。2019年12月25日が2.7のリリース予定日らしい。2.8と2.9はどうなるのか....

2.6 を試したいとき

Dockerで2.6の実行環境が配布されているとのこと。*1

Procの合成

合成演算子というものをつかってProcを合成できるらしい。

感想

とりあえず目に留まったものをメモとして書いてみましたが他にもいろいろと変更点があるようです。

Puppetterの記事

GUIを持たないChromeとそれを扱うライブラリであるPuppeteerの記事。
「note」のタイムラインをスクレイピングするという記事が書かれていました。

私もnoteの記事をスクレイピングしたいと思っていてSeleniumuの勉強を始めたところでした。
PuppeteerとSeleniumの違いとかどうなんだろう。

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)

Selenium実践入門 ―― 自動化による継続的なブラウザテスト (WEB+DB PRESS plus)

筆一本はいかに実現したか

結城先生へのインタビュー記事。何を意識して執筆活動をされているのかが書かれていてとても面白かった。
上記の「note」も結城先生の数学ガールを読むために購読しています。
自分が発信する言葉が相手に伝わってどう解釈されるかを意識しながら書いているという言葉がとても印象に残りました。

記事を読んで、ダウンタウンのまっちゃんが

紳助さんの笑いは耳で聴く笑いやけど、ぼくの笑いは耳で聴いて一回頭でイメージして面白いってなる笑いやから

って話されていたのを思い出しました。
言葉を使って商売されている方はやはり相手にどう伝わるかに対してとても敏感なんですね。
私もこれから意識していきたいと思いました。

感想

全体的に面白い記事が多かったです。買ってよかった!

*1:自分の手元には一応ビルドした2.6は入っている

KLEE を試してみた。

シンボリック実行のツールとして公開されているKLEEを試してみました。

dockerのインストール

KLEEの環境を構築するのはdockerのイメージを使うのが一番手軽に試せるようです。
dockerについてはこちらの記事を参考にインストールしてみました。
qiita.com

dockerイメージの実行

dockerのインストールが完了したらイメージを取得して実行します。


# イメージの取得
$ docker pull klee/klee
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
klee/klee latest f0052ebadb00 7 months ago 1.31GB

# 実行
$ docker run --rm -ti --ulimit='stack=-1:-1' klee/klee

# dockerのコンテナを起動するとシェルが立ち上がります。
klee@684a74481856:~$ pwd
/home/klee
klee@684a74481856:~$ ls
klee_build klee_src

チュートリアル

KLEEの最初のチュートリアルがこちらのサイトで解説されています。
klee.github.io

シンボリック実行を試すコードは以下のようなコードとして示されています。
引数で渡された数値の符号を判定する関数のようです。

/*
 * First KLEE tutorial: testing a small function
 */

#include <klee/klee.h>

int get_sign(int x) {
  if (x == 0)
     return 0;
  
  if (x < 0)
     return -1;
  else 
     return 1;
} 

int main() {
  int a;
  klee_make_symbolic(&a, sizeof(a), "a");
  return get_sign(a);
} 

チュートリアル自体は丁寧に解説が書かれていますのでここでは詳細は書きませんが、注意点として、上記のコンテナのシェル上では、 klee/klee.hは klee_src/include に展開されています。
従って get_sign.c を /home/klee 以下で配置した場合にビットコードにコンパイルするコマンドは


$ clang -I klee_src/include -emit-llvm -c -g get_sign.c

になります。

感想

dockerを普段使うことがないのでとまどいました(というかまだよくわかっていないのでもう少しdockerに対する勉強が必要そうです。
今回は以前に買って積本になっていたdocker教科書第2版が役に立ちました。

プログラマのためのDocker教科書 第2版 インフラの基礎知識&コードによる環境構築の自動化

プログラマのためのDocker教科書 第2版 インフラの基礎知識&コードによる環境構築の自動化

KLEE本体がちゃちゃっとmakeして使えないのは不便な気もしますが、dockerで配布しているということはmacwindowsでも使えるということですよね。KLEEをwindowsでも使えないか試してみたいと思います。

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

Dockerエキスパート養成読本[活用の基礎と実践ノウハウ満載!] (Software Design plus)

xrdpをインストールしてLinuxマシンにリモートデスクトップで接続する。

xrdpをインストールしたときのメモ。OSはLinux Mint を使っていますがUbuntu系であれば恐らく同じだと思います。


$ sudo apt install xrdp vnc4server

$ sudo vi /etc/xrdp/xrdp.ini
# crypt_levelを crypt_level=high に設定

# サービスを再起動
sudo service xrdp restart

インストールとサービスが起動できればあとはリモートデスクトップで接続できます。

C言語の関数の戻り値を実行時に変更する

「試験をしているんだけど、実行中に関数の戻り値を変える方法はないかな?」

と相談を受けました。

C言語単体テストをする場合、システムコールやライブラリ関数はダミーの関数を用意しておいて自作部分の実装をテストすることはよくありますが、実行中にこのダミー関数の振舞いを変えたいという趣旨です。*1

単純にダミー関数の戻り値をグローバル変数にしてしまえば問題はありません。
ただしテストコードが多いと呼び出し元のテストコードが少し複雑になりそうです。

少し考えてみましたが、

DLL,共有ライブラリならできるのでは?

というのが私の思いついた答えでした。

linuxだとdlopen,dlsym,dlcloseを使って、ダミー関数を共有ライブラリにしておけば実際に呼び出される関数を変更することができます。

ということでそれっぽいコードを書いてみました。

試してみたコード

呼び出し元のソースコード(main.c)

#include <stdio.h>
#include <dlfcn.h>
#include <unistd.h>

int add(int a, int b)
{
    int result;
    void *handle_a = dlopen("./liba.so", RTLD_LAZY);
    int(* add_handler)(int, int) = dlsym( handle_a, __FUNCTION__ );

    result = add_handler(a, b);

    dlclose(handle_a);
    return result;
}

int main(int argc, char **argv)
{
    int result = 0;

    while(1)
    {
        result = add(1,2);
        sleep(1);
        printf("%d\n", result);
    }

    return 0;
}

ダミー関数の実装(add.c)

int add(int a, int b)
{
	return a + b;
}

ビルドと実行


$ gcc -shared add.c -o libadd.so
$ gcc main.c -ldl
$ ./a.out
$ 3
$ 3
$ 3
$ 3
....(1秒間隔で出力される)

共有ライブラリのaddが呼び出されてadd(1,2)の結果が正しく(1+2=3)出力されていることは分かります。

さてここからが本番です。
add.cの中身を

int add(int a, int b)
{
	return a * b;
}

のように書き換えてコンパイルしてみましょう。
この時、先ほど実行した a.out は停止せず動かしっぱなしにします。

別ターミナルで、


$ gcc -shared add.c -o libadd.so

すると、実行している a.outの出力はどうなるでしょうか。


3
3
3
3
2
2
2
2

2に変わっています!
ということで実行中に関数の戻り値を変える事ができました。

わざわざ共有ライブラリを動的にロードするのはまどろっこしい気もしますが規模が大きいテストではこちらの手法の方が保守性が高い気もします。

補足

呼び出し側で sleep(1)を呼び出している箇所がありますが、最初sleep無しで試したところセグメンテーションフォルトで a.out は止まってしまいました。
add.cをコンパイルすることによってロードしているライブラリのファイルに対して競合してしまったんだ思います。
sleep(1)を入れて試した限りでは問題なく動作がしましたが、きちんと排他制御を行うのは正しい姿だと思います。

JITコンパイルについて

rubyでも 2.6 からJITコンパイルが取り入れられたそうですが、今回試した実行時にライブラリを差し替えるのはアプローチとしては同じようなイメージになるかと思います。今回のadd関数は1行で終わるシンプルな関数なので面白くありませんが、これが処理時間のかかる関数だったとして実行中に処理時間のかからない関数に差し替える事ができれば嬉しいですよね。実行中のデータなどで関数の処理にフィードバックをかけて最適な処理にしていくといったこともできます。
*2

感想

同じような事は以前にも試してみていたのですが、単体テストなんかで使えるテクニックだとは気づいていませんでした。

mcommit.hatenadiary.com

この手のテクニックはどの言語でも何かしら手段が提供されていると思います。
私の知っている範囲ではC言語ですと、BINARY HACKS、RubyですとメタプログラミングRubyでそういったテクニックが解説されています。

Binary Hacks ―ハッカー秘伝のテクニック100選

Binary Hacks ―ハッカー秘伝のテクニック100選

メタプログラミングRuby 第2版

メタプログラミングRuby 第2版

メタプログラミング.Netという本もあるんですね。面白そうなので読んでみたいと思います。

メタプログラミング.NET (アスキー書籍)

メタプログラミング.NET (アスキー書籍)

動的に何かを変更するのってなんかかっこよくて面白いですね。

*1:正常系のケースを実行した後そのまま異常系を試験したいというような状況ですね

*2:人工知能系のプログラミングもこういった形で実装できそうですね。