QEMUの64bit Windowsホストの対応状況

久しぶりに、Windows 7 x64ホストでQEMUの64 bitバイナリをつくってみました。ところが、なかなか動かせる環境が見つけられなくて試行錯誤したのでまとめてみます。

1. ライブラリのバージョン

gtk+ライブラリは、

ダウンロード先がなくなってしまいました。(2017/06/18追記)

gtk+-bundle_2.16.6-20100208_win64.zip -> NG
gtk+-bundle_2.22.1-20101229_win64.zip -> OK

バージョン2.22.1のものを使いました。バージョン2.16.6は、Pixmanというライブラリが入っていません。また、それを加えてもglibがリンクできませんでした。

SDLとzlibライブラリは、

SDL-1.2.15.tar.gz
zlib-1.2.7.tar.gz

を使いました。

2. GCCとQEMUの関係

GCCのバージョンとQEMUのバージョンで動いたり動かなかったりします。いろいろ組み合わせを試しました。

動作確認は、i386-softmmuのみで行っています。Perlやpod2manとかが必要にならないよう、--disable-docsをつけました。また、CCを最初につけないとエラーになります。

CC=gcc ./configure --target-list=i386-softmmu --disable-docs

makeは最初のほうで並列makeできないので、途中で止めて並列に切り替えて継続しています。

make
Ctl-Cで止める
make -j8

GCCは、MinGW-w64プロジェクトからです。どれを選んでいいのかわからなかったので、いろいろ試しました。
linux.imgは、qemu.orgのリファレンスイメージです。
CD-ROMは、Morphix 0.4のisoイメージを用いました。

動かしてみると、クラッシュしたりアサーションが起こったりいろいろです。
結果に書かれたクラッシュは、プログラム自体が途中で止まってしまう場合です。
Assertion failedというのは、qemu-coroutine-lock.cのqemu_in_coroutine()でアサートが起こっています。
kernel panicというのは、Linuxの起動の最後でkernel panicしてフリーズします。
無限ループというのは、画面が無限ループしています。

GCCのバージョンに分けて結果を並べていきます。

2.1 GCC-4.5.4の場合

mingw-w64-bin_x86_64-mingw_20111101_sezero.zip

QEMU linux.img CD-ROM
v1.2.0 無限ループ 無限ループ
v1.4.0 OK OK
v1.6.0 OK OK
v1.7.0 kernel panic Assertion failed

2.2 GCC-4.6.3の場合

x86_64-w64-mingw32-gcc-4.6.3-2-release-win64_rubenvb.7z

QEMU linux.img CD-ROM
v1.2.0 クラッシュ クラッシュ
v1.4.0 クラッシュ OK
v1.5.0 OK クラッシュ
v1.6.0 OK Assertion failed
v1.7.0 kernel panic Assertion failed

2.3 GCC-4.6.4の場合

x64-4.6.4-release-win32-sjlj-rev0.7z

QEMU linux.img CD-ROM
v1.2.0 クラッシュ クラッシュ
v1.4.0 クラッシュ OK
v1.6.0 OK Assertion failed
v1.7.0 kernel panic Assertion failed

2.4 GCC-4.8の場合

x86_64-w64-mingw32-gcc-4.8-stdthread-win64_rubenvb.7z

QEMU linux.img CD-ROM
v1.4.0 クラッシュ OK
v1.6.0 OK Assertion failed
v1.7.0 kernel panic Assertion failed

2.5 番外編GCC-4.6.3 32bitの場合

i686-w64-mingw32-gcc-4.6.3-2-release-win32_rubenvb.7z
gtk+-bundle_2.24.8-20111122_win32.zip

QEMU linux.img CD-ROM
v1.2.0 クラッシュ OK
v1.4.0 クラッシュ OK
v1.5.0 OK クラッシュ
v1.6.0 OK Assertion failed
v1.7.0 OK Assertion failed
v2.0.0-rc1 OK Assertion failed

3. 結果のまとめ

QEMUのバージョンは、古すぎても新しくてもだめでした。バージョン1.4.0から1.6.0が動くようです。でも、ハードディスクとCD-ROMが同時に動く組み合わせがとても少ない。GCC-4.5.4を使ったときだけとなってしまいました。

QEMUのバージョン1.6.0から1.7.0に変わってkernel panicが起こっています。CPUのエミュレートに何かバグが入ったようです。

4. MSYSの並列make

最新のMSYSをmingw-get-setup.exeから入れると、並列makeができなくなってしまいました。msys-1.0.dllのバージョン1.0.18の問題らしくて、msysCORE-1.0.17-1-msys-1.0.17-bin.tar.lzmaの中のバージョン1.0.17のmsys-1.0.dllに差し替えるとうまくいきます。

また、makeの時にversion.oができるまでは並列makeをしてしまうとハングしてしまい、make distcleanすることもできなくなります。最初はmakeのみで始めて、しばらくしてからCtrl-Cで止めてから、make -j8とかで並列makeするとうまくいきます。

5. 感想

なんだか動いたり動かなかったりで、ロシアンルーレットみたいでした。最初に動く組み合わせを見つけるのにとても苦労しました。何が問題だかわからなかった。

CPUに関するバグと、CD-ROMが動かないバグの2つがあるみたいです。CPUに関するものは、ログとにらめっこする必要がありそうです。CD-ROMは、coroutineというもののようですが、どうやったらバグをつぶせるのでしょう。

6. 補足(2014/6/10)

ハードディスクの問題はパッチが出てコミットもされたので、次のリリースは大丈夫だと思います。

CD-ROMの問題は、configure時に--disable-coroutine-poolをつけてコンパイルしてねということのようです。

ところが、g_pollに加えられた変更のため、ハードディスクからまったく起動しなくなっています。パッチは出ているのでそのうち直ると思います。

7.補足2(2014/07/07)

ハードディスクとCD-ROMのバグが両方とれました。
64bitと32bitのバイナリで、ハードディスクとCD-ROMイメージから起動することを確認しました。

マウスのポインタがうまく動いてないですけど、それはまた別の問題ですね。

Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する

この記事は、
Windows 7上のVMware Playerにgdbでリモート接続する方法
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する
の1つです。

1. 実際にやってみると

実際にリモートデバッグをしてみると、いちいちセクションの情報を確認して値を設定することが非常に面倒なことがわかると思います。そこで、ゲストOS上でカーネルモジュールをロードする処理と、ホストOS上でgdbを設定する処理をシェルスクリプトで自動化してみます。

基本的にやっていることは、手作業でやっていたことを自動化しただけです。セクション情報をtext.txt、data.txt、bss.txtに保存して、それをゲストOSからホストOSにコピーします。それを使って、gdbの設定を行います。

2. プログラムの説明

load.shは、ゲストOS上でカーネルモジュールをロードするときに使います。

load.sh ---------------------------------------------------

#!/bin/bash

# modnameにカーネルモジュールの.koの名前を入れます。
modname='testmodule'

# devnameは、デバイスファイルの名前です。
devname='/dev/testmodule'

# my_majorにメジャー番号を入れます。
my_major=88

# locationは、VMwareで設定したホストOSにカーネルモジュールやソースをコピーするときの共有フォルダの名前です。
location='/mnt/hgfs/kazu'

# カーネルモジュールが存在すればアンロードします。
if [ -e /sys/module/$modname ]; then
    sudo rmmod $modname
fi

# カーネルモジュールをロードします。
sudo insmod ${modname}.ko

# デバイスファイルがなければ作ります。
if [ ! -e $devname ]; then
    sudo mknod $devname c $my_major 0
    sudo chmod a+rw $devname
fi

# セクション情報を保存します。
sudo cat /sys/module/${modname}/sections/.text >text.txt
sudo cat /sys/module/${modname}/sections/.data >data.txt
sudo cat /sys/module/${modname}/sections/.bss >bss.txt

# セクション情報をコピーします
cp text.txt $location
cp data.txt $location
cp bss.txt $location

# モジュールとソースコードをコピーします。
cp ${modname}.c $location
cp ${modname}.ko $location

--------------------------------------------------

gd.shは、ホストOS上でgdbをリモート接続するときに使います。break_pointに停止させたい関数の名前を設定します。

gd.sh ---------------------------------------------------------

#!/bin/bash
# gdb port number is required
# ./gd.sh number

# モジュールの名前をセットします。
module_name='testmodule'

# ソースの位置をセットします
source_location='/home/kazu/test'

# ブレークポイントを設定します。
break_point='my_ioctl'

# セクション情報を読み込みます。
text=`cat text.txt`
data=`cat data.txt`
bss=`cat bss.txt`

# gdbコマンドを.gdbinitファイルに設定します。
cat <<EOF >.gdbinit
set architecture i386:x86-64
target remote localhost:$1
add-symbol-file ${module_name}.ko $text -s .data $data -s .bss $bss
break $break_point
EOF

# ソースコードを設定した位置にコピーします。
cp ${module_name}.c $source_location

# gdbを実行します。
gdb

----------------------------------------------------

3. 使い方

ゲストOS上に、load.shを置いてください。そこで、

Ubuntu$ chmode +x load.sh

で実行可能にします。これは1度だけやればいいです。

ソースコードを編集した後、makeの後でload.shすることでモジュールのロードと、コピーがされます。

Ubuntu$ make
Ubuntu$ ./load.sh

ホストOSのCygwin上でgd.shを接続するポート番号とともに起動します。

Cygwin$ ./gd.sh 8864

GNU gdb (GDB) 7.6.50.20130728-cvs (cygwin-special)
Copyright (C) 2013 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.  Type "show copying"
and "show warranty" for details.
This GDB was configured as "x86_64-pc-cygwin".
Type "show configuration" for configuration details.
For bug reporting instructions, please see:
<http://www.gnu.org/software/gdb/bugs/>.
Find the GDB manual and other documentation resources online at:
<http://www.gnu.org/software/gdb/documentation/>.
For help, type "help".
Type "apropos word" to search for commands related to "word".

The target architecture is assumed to be i386:x86-64
0xffffffff8104d386 in ?? ()
add symbol table from file "testmodule.ko" at
        .text_addr = 0xffffffffa03da000
        .data_addr = 0xffffffffa03dc000
        .bss_addr = 0xffffffffa03dc360
Breakpoint 1 at 0xffffffffa03da0f1: file /home/kazu/test/testmodule.c, line 51.
(gdb)

このような表示になって、ゲストOSの実行が止まれば成功です。この状態でcontinueします。

(gdb) continue

その後、ゲストOS上でtestdriverを動かします。

Ubuntu$ ./testdriver

そして、次のようにmy_ioctlで実行が止まれば成功です。

Breakpoint 1, my_ioctl (file=0xffff8800240cdec0, count=32,
    buf=18446612132972596240) at /home/kazu/test/testmodule.c:51
warning: Source file is more recent than executable.
51      {
(gdb)

あとは、手作業で変数の値を見たりできます。面倒な作業が自動化できれば、カーネルモジュールの開発に集中できると思います。

4. 参考にしたサイト

この記事を作るときに、非常に多くのサイトを参考にさせてもらいました。ありがとうございます。もし、何か問題があったら、これらのサイトを参考にしてみてください。

リモートデバッグについて
VMwareのGDB機能を利用する
LinuxでLoadable Moduleをデバッグする
VMware
Qemu and the Kernel
The Kernel Newbie Corner: Kernel and Module Debugging with gdb
Debugging kernel modules
Debug-info for loadable kernel modules
■[Debian] VMwareでLinuxカーネルのデバッグ
Debugging Linux kernels with Workstation 6.0
Debugging Linux Kernel in VMWare with Windows host

カーネルモジュールについて
CS 686: Special Topic: Intel EM64T and VT Extensions (Spring 2007)

gdbの使い方について
gdbコマンド メモ
gdb の使い方・デバッグ方法まとめ

シェルスクリプトについて
UNIX & Linux コマンド・シェルスクリプト リファレンス

Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする

この記事は、
Windows 7上のVMware Playerにgdbでリモート接続する方法
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する
の1つです。

1. カーネルモジュールとそのソースコードをWindowsホストに送り込む

gdbを使ってカーネルモジュールをリモートデバッグするためには、モジュールそのものであるtestmodule.koとそのソースファイルであるtestmodule.cをWindows上で参照する必要があります。testmodule.koが持つシンボル情報、関数の位置が必要になることと、ソースコードを表示するためにソースコードが必要になるためです。

今回は、共有フォルダーを使って、その2つをWindowホストに送り込みます。VMware Playerの設定でkazuというディレクトリがCygwinの/home/kazuを指し示しているとします。Ubuntu 13.04上でファイルを2つコピーします。共有フォルダは/mnt/hgfs/kazuにマウントされているとします。

ubuntu$ cp testmodule.ko /mnt/hgfs/kazu/
ubuntu$ cp testmodule.c /mnt/hgfs/kazu/

testmodule.koというモジュールは、gdbを起動するディレクトリに置きます。
また、testmodule.cというソースは、Cygwin上でLinuxのソースディレクトリと全く同じ位置に置きます。
つまり、Ubuntu 13.04上で/home/kazu/testというディレクトリでモジュールを作った場合、Windows上のCygwinで/home/kazu/testというディレクトリにtestmodule.cはなくてはいけません。

cygwin$ cp testmodule.c /home/kazu/test/

このとき、今回はやっていませんが、共有フォルダをうまく使うとWindows上でコピーする手間が省けます。

2. カーネルモジュールのロードされた位置情報を確認する

ゲストOS上で、/sys/module/モジュール名/sections/ディレクトリにある.text、.data、.bssというファイルにカーネルモジュールの各セクションの開始アドレスが保存されています。それを確認します。このときのポイントは、スーパーユーザーでファイルを見ることです。そうしないと、0x0000000000000000という値になって確認できません。

ubuntu$ sudo cat /sys/module/testmodule/sections/.{text,data,bss}
0xffffffffa030c000
0xffffffffa030e000
0xffffffffa030e360

上から順に、.text、.data、.bssセクションのアドレスになります。.textセクションには、関数の開始アドレスが入っています。C言語のソースコードで、関数の外側にあるグローバル変数は.bssセクションに値が保存されます。また、初期値が設定してあるものは.dataセクションに値が保存されます。そのため、gdbで値を表示するためには、それらのセクションの先頭アドレスを指定しないと値を表示することができません。

3. gdbでリモートデバッグを開始する

今度は、ホストOSのCygwinのターミナルで、gdbを起動します。set architectureでターゲットとなるアーキテクチャーが64bitであることを設定します。target remoteで接続します。ポート番号は、最初は8864で、2回目からは8865になります。

cygwin$ gdb
(gdb) set archi i386:x86-64
The target architecture is assumed to be i386:x86-64
(gdb) tar rem :8864
Remote debugging using :8864
0xffffffff8104d386 in ?? ()

次に、add-symbol-fileというgdbのコマンドで、カーネルモジュールのシンボル情報をロードします。このとき、-sオプションを使って先に確認した.dataと.bssセクションの開始アドレスを使います。アドレスや、セクション名を間違えやすいので注意してください。

(gdb) add-symbol-file testmodule.ko 0xffffffffa030c000 -s .data 0xffffffffa030e000 -s .bss 0xffffffffa030e360

Reading symbols ...done.と表示されたら成功です。

add symbol table from file "testmodule.ko" at
        .text_addr = 0xffffffffa030c000
        .data_addr = 0xffffffffa030e000
        .bss_addr = 0xffffffffa030e360
(y or n) y
Reading symbols from /cygdrive/c/mingw64/msys/1.0/home/kazu/testmodule.ko...done.
(gdb)

次に、停止させたい関数の名前をブレークポイントに登録します。今回は、my_ioctlで停止させます。

(gdb) break my_ioctl
Breakpoint 1 at 0xffffffffa030c0f1: file /home/kazu/test/testmodule.c, line 51.
(gdb)

ブレークポイントが設定されました。
この状態でcontinueでゲストOSに制御を移します。

(gdb) continue

ゲストOSでの操作が可能になったと思います。そして、Ubuntuの中でtestdriverを動かしてみます。testdriverは、ioctl()を使ってカーネルモジュールを呼び出します。

Ubuntu$ ./testdriver

すると、ゲストOSの実行が停止して、ホストOSのCygwin上で次のように表示されます。

Breakpoint 1, my_ioctl (file=0xffff880027111ec0, count=32,
    buf=18446612133350149392) at /home/kazu/test/testmodule.c:51
warning: Source file is more recent than executable.
51      {
(gdb)

ブレークポイントで停止します。このとき、ソースコードをコピーして使っているためにwarningがででいますが、無視してください。これで、カーネルのソースコードを表示したり変数を確認することが可能になりました。

4. カーネルの変数を確認する

まず、listコマンドでソースコードを表示してみます。

(gdb) list
46              printk("<1>\nRemovind %s \n", modname);
47      }
48
49      // ユーザーモードのプログラムで、ioctl()を使ったときに呼び出されます。
50      long my_ioctl( struct file *file, unsigned int count, unsigned long buf)
51      {
52              int i;
53
54              i = 1;
55              global_data = 2;
(gdb)

では、1行ずつ実行してみましょう。nextまたは、nで実行できます。1行実行した前後で、countの値を見てみます。

(gdb) print count
$1 = 32
(gdb) next
54              i = 1;
(gdb) print count
$2 = 100
(gdb)

ブレークポイントで止まった直後は、引数の値が設定されていないために、countは32でした。1行実行して関数の中に入って100に変わりました。この100は、testdriver.cでioctl()を呼んだとき、100バイトと指定してあったものです。また、bufの内容を見てみましょう。x/sと打ち込みます。xは、メモリーを検査するコマンドで、sで文字列として表示します。

(gdb) x/s buf
0x7fff0e9c3b10: "Hello!"
(gdb)

testdriver.cで設定したHello!の文字が見られます。もしくは、print (char *)bufとすることでも表示できます。(char *)にキャストするのは、my_ioctlでbufは、unsigned longになっているためです。

(gdb) print (char *)buf
$15 = 0x7fff0e9c3b10 "Hello!"
(gdb)

つぎに、ローカル変数iとグローバル変数global_dataの変化を調べます。まず、iとglobal_dataを表示しても値が設定されていないので値は何が入っているかわかりません。

(gdb) print i
$2 = -1
(gdb) print global_data
$3 = 0
(gdb)

1行実行すると、iに値が設定されていきます。このとき、55行目のソースコードが表示されますが、これは現在実行したコードではなく、次に実行するコードという意味です。

(gdb) next
55              global_data = 2;
(gdb) print i
$4 = 1
(gdb)

iが1に設定されました。また実行すると、global_dataに値が設定されます。

(gdb) next
56              printk("<1>\n%s is called! \n", modname);
(gdb) print global_data
$5 = 2
(gdb)

global_dataが2になりました。このように1行ずつ実行して値の変化を調べることができます。

4. リモートデバッグの終了

disconnectコマンドで、接続を切断してquitで終了します。

(gdb) disconnect
(gdb) quit

で終了できます。

5. 再接続

add-symbol-fileは、シンボルの再ロードに対応していないようです。ソースコードを修正してもう1度デバッグをするためには、gdbを終了してからもう一度接続する必要があります。

ソースコードを変更した後は、コンパイルします。その後、ロードされているモジュールをアンロードしてから新しいモジュールをロードします。セクション情報を確認します。

Ubuntu$ make
Ubuntu$ sudo rmmod testmodule
Ubuntu$ sudo insmod testmodule.ko
Ubuntu$ sudo cat /sys/module/testmodule/sections/.{text,data,bss}
Ubuntu$ cp testmodule.ko /mnt/hgfs/kazu/
Ubuntu$ cp testmodule.c /mnt/hgfs/kazu/

ソースコードを配置した後、gdbを立ち上げてもう一度同じことをします。ソースコードを変更してカーネルモジュールを再ロードすると、add-symbol-fileのセクション情報が毎回変わってきますので注意してください。

$ cp testmodule.c /home/kazu/test/
$ gdb
(gdb) set archi i386:x86-64
(gdb) tar rem :8865
(gdb) add-symbol-file testmodule.ko 0xffffffffa030c000 -s .data 0xffffffffa030e000 -s .bss 0xffffffffa030e360

このような流れになります。
このときVMware Playerのバグで、8865番になっていることに注意してください。一度8865番になると次は変わらないようです。接続できないときは、起動したゲストOS用の.logファイルを確認してください。

6. うまくリモートデバッグできないときに確認すること

まず、gdbがうまく接続できているかどうか。
.vmxファイルにgdbstubの設定がされているか。
gdbを動かしたときのポート番号があっているか.logファイルで確認する。

ソースコードがgdb上でうまく表示されないときに確認すること。
ソースコードが-g -O0オプションつきでコンパイルしているか。Ubuntu 12.04とUbuntu 12.10では、-O0オプションをつけてコンパイルすることはできませんでした。
ソースコードはコンパイルしているか。
カーネルモジュールはホストOSにちゃんとコピーできているか。
ソースコードを変更したときは、ゲストOSからホストOSにちゃんとコピーできているか。
ソースコードは、LinuxとCygwinで同じ位置に置かれているか。
add-symbol-fileでセクション情報を正確に設定できているか。ソースを変更したときは、gdbを再起動しないといけません。セクション情報も変わっているはずです。

いろいろと落とし穴があります。このようなことをチェックしてください。

次に続きます。
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する

Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する

この記事は、
Windows 7上のVMware Playerにgdbでリモート接続する方法
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する
の1つです。

まず、gdbがVMwareにリモート接続できることを確認してください。

1. デバッグに使うソースコードの説明

今回は3つのソースを用意しました。testmodule.cというモジュールを作りました。このモジュールは、メジャー番号88番でキャラクターデバイスを登録してioctlを処理するモジュールです。
また、Makefileは、そのモジュールをコンパイルするための物です。testdriver.cは、モジュールをテストするために使うユーザーモードのプログラムです。

testmodule.cは次の通りです。モジュールをロードするときに呼ばれるinit_module()とアンロードしたときに呼ばれるcleanup_module()があります。また、ユーザーモードのプログラムでioctl()を実行されたときに呼び出されるmy_ioctl()という関数があります。

file_operations構造体のunlocked_ioctlというメンバー変数にmy_ioctlを設定してregister_chardev()で登録することで、ユーザーモードのプログラムがioctl()を使ったときにカーネルモジュールのmy_ioctl()が実行されます。

init_module()内でregister_chardev()を呼び出す事でメジャー番号88番のキャラクターデバイスを登録しています。また、cleanup_module()のunregister_chardev()でデバイスの登録を解除しています。

testmodule.c -------------------------------------------------------

/*
  testmodule.c
  Linux test driver kernel module
  character device major=88 minor=0

  sudo insmod testmodule.ko
  sudo mknod /dev/testmodule c 88 0
  sudo chmod a+rw /dev/testmodule
*/

#include &amp;amp;amp;lt;linux/module.h&amp;amp;amp;gt;
#include &amp;amp;amp;lt;linux/fs.h&amp;amp;amp;gt;		// for struct file_operations

// modnameにモジュールの名前を設定します。また、my_majorの88番がデバイスの番号です。
char modname[] = &amp;amp;amp;quot;testmodule&amp;amp;amp;quot;;
int my_major = 88;

long my_ioctl( struct file *, unsigned int, unsigned long );

// ユーザーモードのプログラムがioctl()を使ったときにmy_ioctl()を呼び出すよう設定します。
struct file_operations
my_fops =	{
		owner:		THIS_MODULE,
		unlocked_ioctl:		my_ioctl,
		};

// グローバル変数です。
int global_data;

// モジュールをロードしたときに呼び出されます。
int init_module(void)
{
	printk(&amp;amp;amp;quot;\nInstalling %s \n&amp;amp;amp;quot;, modname);
	printk( &amp;amp;amp;quot;(major=%d) \n&amp;amp;amp;quot;, my_major );

	// 設定した名前と番号で、キャラクターデバイスを登録します。
	return register_chrdev( my_major, modname, &amp;amp;amp;amp;my_fops );
}

// モジュールをアンロードしたときに呼び出されます。
void cleanup_module(void)
{
	// キャラクターデバイスの登録を解除します。
	unregister_chrdev( my_major, modname );

	printk(&amp;amp;amp;quot;\nRemovind %s \n&amp;amp;amp;quot;, modname);
}

// ユーザーモードのプログラムで、ioctl()を使ったときに呼び出されます。
long my_ioctl( struct file *file, unsigned int count, unsigned long buf)
{
	int i;

	i = 1;
	global_data = 2;
	printk(&amp;amp;amp;quot;\n%s is called! \n&amp;amp;amp;quot;, modname);

	return 0;
}

MODULE_LICENSE(&amp;amp;amp;quot;GPL&amp;amp;amp;quot;);

-------------------------------------------------------------------

これをコンパイルするMakefileは、次の通りです。

Makefile ------------------------------------------------

ifneq	($(KERNELRELEASE),)
obj-m	:= testmodule.o
ccflags-y	:=-g -O0

else
KDIR	:= /lib/modules/$(shell uname -r)/build
PWD	:= $(shell pwd)
default:
	$(MAKE) -C $(KDIR) SUBDIRS=$(PWD) modules
	rm -r -f .tmp_versions *.mod.c .*.cmd *.o Module.symvers modules.order

endif

----------------------------------------------------------

Linuxのバージョン3.0になって、Makefileの作り方が少し変わったようです。KERNELRELEASEに値が設定されているかどうかで、2つの部分に分かれます。カーネルを作る時のkbuildというシステムは、KERNELRELEASEに値が設定されているときだけを見るようです。値がないときの設定は、ふつうのmakeが処理するのだそうです。モジュールをコンパイルするときのフラッグを設定するときに、以前はEXTRA_CFLAGSを使っていたと思いますが、ccflags-yに変わったようです。もしくは、モジュールの名前をつけてtestmodule-yとします。
ccflags-yに-gをつけることで、デバッグ情報をカーネルモジュールに埋め込みます。
また、-O0(オーとゼロ)をつけることで、gdb上でステップ実行したときに1行ごとに実行することができます。

このカーネルモジュールをテストするユーザーモードのプログラムが、次のtestdriver.cです。
/dev/testmoduleというデバイスを開いて、ioctl()でHello!という文字列を送り込みます。

testdriver.c -----------------------------------------------

/*
  testdriver.c
  Test testmodule.c
*/

#include  // for printf(), perror()
#include  // for open()
#include  // for exit()

char devname[] = &amp;amp;amp;quot;/dev/testmodule&amp;amp;amp;quot;;

int main(void)
{
    int fd;
    int retval;
    char buf[100] = &amp;amp;amp;quot;Hello!&amp;amp;amp;quot;;

    fd = open(devname, O_RDWR);
    if (fd &amp;amp;amp;lt; 0) {
        perror(devname);
        exit(1);
    }
    retval = ioctl(fd, sizeof(buf), &amp;amp;amp;amp;buf);

    return 0;
}

---------------------------------------------------------------

2. ゲストOSでLinuxカーネルモジュールを作る

ゲストOS上でLinuxカーネルモジュールをつくります。

3つのファイルを、Ubuntu 13.04内の同じディレクトリにおいてください。そしてmakeします。

$ make

これでtestmodule.koというファイルができたら成功です。

3. ユーザーモードのプログラムを作る

ゲストOS上で、ユーザーモードのプログラムを作ります。

$ gcc testdriver.c -o testdriver

とすることで、testdriverというプログラムができれば成功です。

4. 動作確認をする

まず、カーネルモジュールをロードします。モジュールのロードには、スーパーユーザーの権限が必要ですのでsudoを使います。insmodでロードできます。また、ロードされたかどうかlsmodで確認できます。

$ sudo insmod testmodule.ko
$ lsmod |grep testmodule
testmodule 12952 0

次に、ユーザーモードのプログラムからカーネルモジュールにアクセスするためのデバイスファイルを作ります。testmodule.cで登録したキャラクターデバイスで88番のデバイスファイルを作ります。ユーザーモードのプログラムがアクセスできるように読み書きの権限をつけておきます。

$ sudo mknod /dev/testmodule c 88 0
$ sudo chmod a+rw /dev/testmodule

これで、準備はできたので、testdriverをうごかします。

$ ./testdriver

なにもエラーが表示されなければ成功です。本当に実行されたかどうか、ログファイルで確認することができます。less /var/log/kern.logと打って出てきた画面で大文字のGを押してください。ログファイルの最後尾を見ることができます。

$ less /var/log/kern.log

Apr 11 20:51:22 Mars kernel: [ 1913.498362] Installing testmodule
Apr 11 20:51:22 Mars kernel: [ 1913.498366] (major=88)
Apr 11 20:56:28 Mars kernel: [ 2219.260167]
Apr 11 20:56:28 Mars kernel: [ 2219.260167] testmodule is called!

このように、testmodule is called!と表示されているはずです。

5. 後始末をする

ロードしたカーネルモジュールは、ゲストOSを再起動するとロードされなくなりますが、次のようにするとアンロードできます。

$ sudo rmmod testmodule

また、作ったデバイスファイルは削除することができます。

$ sudo rm /dev/testmodule

これで、リモートデバッグをするカーネルモジュールを用意することができました。

次に続きます。
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする

Windows 7上のVMware Playerにgdbでリモート接続する方法

この記事は、
Windows 7上のVMware Playerにgdbでリモート接続する方法
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(2) - リモートデバッグする
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(3) - 自動化する
の1つです。

1. 用意するもの

Windows 7上のVMware Playerで、Linuxのデバイスドライバをリモートデバッグしてみました。デバッグするのに少しコツが必要なので紹介します。

64bitのデバッグをしてみたかったので、全部64bitでそろえてみました。Windows 7は、x64版です。VMware Playerは、32bitと64bitの区別がないようでした。Cygwinは、setup-x86_64.exeをつかって、64bit版をインストールしました。gdbのみ使います。Windows上のgdbとしては、MinGW-x64の物もあるのですが、Ctrl-CでゲストOSの実行を止められないのでCygwinのものを使いました。バージョンによっては、Mingwの物でもいいかもしれません。ゲストOSとしては、Ubuntu 13.10 64bit版をインストールしました。

Windows 7 x64 (ホスト)
VMware Player 6.0.1
Cygwin 64bit
Ubuntu 13.10 64bit (ゲスト)

Cygwinは、32bitでもいいと思いますが、確かめていません。

2. VMware Playerの設定

WindowsにVMware PlayerとCygwinをインストールします。その後、VMware PlayerにUbuntuをインストールします。
全部インストールが終わったところで、ゲストOSの設定ファイルの.vmxファイルに以下の設定を追加します。

debugStub.listen.guest64 = &quot;TRUE&quot;
debugStub.listen.guest64.remote = &quot;TRUE&quot;
debugStub.hideBreakpoints = &quot;TRUE&quot;
monitor.debugOnStartGuest64 = &quot;TRUE&quot;

これでVMware Playerを起動すると、サーバーとして通信していいかを聞くダイアログボックスが現れると思います。許可します。これで、ポート8864番でgdbの接続を待ってくれます。複数のゲストを同時に立ち上げたときは、ポートの番号が8865、8866と増えていきます。もし、32bitのゲストOSの場合は、guest64のところがguest32になって8832番になります。

3. gdbの接続の確認

ゲストOSを起動します。そうしたら、Cygwinのコンソールで、gdbを起動します。そのgdbの中で以下のコマンドを打ち込みます。

(gdb) set architecture i386:x86-64
(gdb) target remote localhost:8864

コマンドは、短縮が可能で以下でもいいです。

(gdb) set archi i386:x86-64
(gdb) tar rem :8864

set architectureは、これからデバッグするゲストOSのモジュールが64bitの物であることを指示します。target remoteは、VMware Playerにポート番号8864番で接続してくださいという命令です。

(gdb) set architecture i386:x86-64
The target architecture is assumed to be i386:x86-64
(gdb) tar rem :8864
Remote debugging using :8864
0xffffffff8104d386 in ?? ()
(gdb)

こんな風に、0xffffffffなんとかと出て、ゲストOSの実行が止まってフリーズしたら成功です。

4. ゲストOS実行の再開

Cygwinのコンソールで、cまたは、continueと打ち込みます。

(gdb) c

とすると、ゲストOSが動き出して操作できるようになると思います。

また、CygwinのコンソールでCtrlキーとCキーを同時に押すと、また0xfffffffffなんとかとでて止まります。

(gdb) c
Continuing.

Program received signal SIGINT, Interrupt.
0xffffffff8104d386 in ?? ()
(gdb)

5. 終了の仕方

gdbで接続している状態で、gdbをquitしてしまうと、ゲストOSが強制終了してしまいます。まず、gdbの接続を切断します。disconnectと打つと次のようになります。disconnectの短縮形はdisconまで打ち込んでください。disだけだと、disableと混同してしまいます。

(gdb) disconnect
Ending remote debugging.
(gdb)

その後でゲストOSを終了します。

6. 再接続の仕方

gdbは、再接続することもできます。

(gdb) tar rem :8865

と打ち込みます。このとき、VMware Player 6.0.1には1度disconnectした後で、ポートが8865になるバグがあるようです。最新の.logファイルで確認することができます。

2014-04-09T16:47:19.737+09:00| vmx| W110: Debug stub: VMware Player is listening for debug connection on port 8865.

一度8865番になると、その後は変わらないようです。その場合は、

(gdb) disconnect
Ending remote debugging.
(gdb) tar rem :8865

とすることでもう1度つなげることができます。

なお、32bitの場合は、8832が8833番になるようです。.logファイルで確認してください。

次のようにすると終了できます。

(gdb) disconnect
Ending remote debugging.
(gdb) quit

次の記事に続きます。
Windows 7上のVMware PlayerでLinuxのデバイスドライバをリモートデバッグする方法(1) - デバッグするカーネルモジュールを用意する

Windows 7上のVMware PlayerでUbuntu 12.04の上でKVMを動かす

今まで、VirtualBoxでUbuntu 12.04を動かしていました。そのUbuntuの上でKVMを動かしてみたくなって試したら、あっさり動きました。

まず、ハードディスクイメージのvdiのファイルをvmdkに変換しました。

VBoxManage clonehd --format VMDK "Ubuntu 12.04 x64.vdi" "Ubuntu 12.04 x64.vmdk"

その後で、VMware Playerで新しく仮想マシンを作りました。このとき、ハードディスクは分割しないようにしました。そして、できたハードディスクのイメージ.vmdkファイルをUbuntu 12.04 x64.vmdkにとりかえたらうまく起動しました。

VMware Player上でVT-xを有効にするのはCPUの設定をするだけでした。 ”仮想マシン設定の編集”で”プロセッサ”を選び、”Intel VT-x/EPTまたはAMD-V/RVIを仮想化”をチェックしました。

その後で、ゲストOS上でKVMのドライバーをロードしました。

$ sudo modprobe kvm_intel

そして、QEMUを動かしたらちゃんとkvmが有効になっていました。カーネルモジュールの自動起動をどうしたらいいかいまいちわからないけど。

これで、OpenStackとかCloudStackとかOpenFlowとかいろいろ試せます。

参考にしたサイトは、
VirtualBoxのvdiからVMware Playerのvmdkへ変換
VMwarePlayer+Ubuntu/KVM/OVSでOpenFlowテスト環境をつくる(1)
です。ありがとうございます。

Bionic as a Service

「600万ドルの男」というテレビの番組を覚えていますか。僕は、「バイオニックジェミー」の方の印象が強いのですが。だいぶ年配の方でないと見たことはないでしょうけど、スティーブ・オースティンという宇宙飛行士が着陸の失敗で、目や手足を機械に置き換えたサイボーグとして活躍するテレビシリーズでした。

そんな夢物語が、だんだんと現実になってきているということが記事になっていました。

Bionic as a Service

最近では、心拍数や血圧をクラウドのデータベースにアップロードすることがされていますよね。そういうのが、現実化の1歩ではないかと。

Intelligenceという番組は、脳にチップを埋め込んでインターネットからテレビからあらゆる情報にアクセスできるという番組だそうです。これも、脳に埋め込むことは遠い話かもしれないけれど、Google Glassができていることを考えると、そう遠い話しでもないかも。

頭の中で話しかけると、Siriが答えるのも夢でないかも。

AI as a Serviceとしては、IBMがWatsonというシステムをクラウドに持ち込んでいるそうです。

そんな楽しい記事になっていました。