2013年3月12日火曜日

Windows8 64bit上での Cygwin

Windows8 64bit 環境上で Cygwin 1.7 を使い、make を走らせると、fork に失敗するという(致命的な)現象が起きる。
例えば、こんな感じ。

$ make
gcc -std=gnu99 `test -f 'gen-fac_ui.c' || echo '../gmp-5.0.1/'`gen-fac_ui.c -o gen-fac_ui.exe
      1 [main] gcc-4 2412 fhandler_disk_file::fixup_mmap_after_fork: requested 0xFFE90000 != 0x0 mem alloc base 0x0, state 0x10000, size 65536, Win32 error 487
   1132 [main] gcc-4 2412 C:\Cygwin\bin\gcc-4.exe: *** fatal error in forked process - recreate_mmaps_after_fork_failed
   3101 [main] gcc-4 2412 open_stackdumpfile: Dumping stack trace to gcc-4.exe.stackdump
      1 [main] gcc 1480 fork: child -1 - forked process 2412 died unexpectedly,retry 0, exit code 256, errno 11
      0 [main] collect2 5364 fhandler_disk_file::fixup_mmap_after_fork: requested 0xFFE90000 != 0x0 mem alloc base 0x0, state 0x10000, size 65536, Win32 error 487
    596 [main] collect2 5364 C:\Cygwin\lib\gcc\i686-pc-cygwin\4.5.3\collect2.exe: *** fatal error in forked process - recreate_mmaps_after_fork_failed
   1354 [main] collect2 5364 open_stackdumpfile: Dumping stack trace to collect2.exe.stackdump
      1 [main] collect2 7144 fork: child -1 - forked process 5364 died unexpectedly, retry 0, exit code 256, errno 11
./gen-fac_ui 32 0 >mpz/fac_ui.h || (rm -f mpz/fac_ui.h; exit 1)


/bin/ash した後で /bin/rebaseall をすれば良いという意見を見掛けたが、自分の環境では

$ export LANG=C

を入力する事で治った。

参照したのはこちら。
Windows8 上で cygwin 版の git で pull/push/fetch に失敗する

2012年11月19日月曜日

BSDの寄り道#3 WordPress の導入

WordPressをOpenBSD上で動作させ、ブログのサーバーとして稼働し始めた。

バックエンド(と言うのかな、ブログデータの管理を指す)に MySQL を立ち上げ、root では無いユーザーを定義(アカウントは内緒にするのが一般的でしょう)。
で、WordPress で使う database を定義しておき、あとは WordPress のインストーラ任せ。

参考にしたのはこちら 「WordPress_のインストール

レンタルサーバーでは無いので、「MySQL クライアントの利用」を参照し、順番に設定していった。

で、現在、「OpenBSDを使う!」が動き出している。このページを使って OpenBSD そのもののインストールや、各種アプリケーションの導入について、ここよりも詳しく紹介していきたいと思う。



2012年11月13日火曜日

BSDの寄り道#2 apache2 + PHP5

OpenBSD+Apache2+PHP5 の場合、apache2 の httpd.conf に PHP モジュールの設定をすると、以下のメッセージが出力される場合がある。
# apachectl2 start
/usr/local/sbin/httpd2:/usr/local/lib/php-5.3/libphp5.so: undefined symbol 'ap_block_alarms'
/usr/local/sbin/httpd2:/usr/local/lib/php-5.3/libphp5.so: undefined symbol 'ap_unblock_alarms'
:
:
:
/usr/local/sbin/httpd2:/usr/local/lib/php-5.3/libphp5.so: undefined symbol 'ap_reset_timeout'
/usr/local/sbin/httpd2:/usr/local/lib/php-5.3/libphp5.so: undefined symbol 'ap_unblock_alarms'
httpd2: Syntax error on line 117 of /etc/apache2/httpd2.conf: Cannot load /usr/local/lib/php-5.3/libphp5.so into server: Cannot load specified object

これは PHP5 が Apache1.3用に Build されている為、ライブラリの参照に失敗しているようだ。
対策として、OpenBSD の Package ではなく Ports でのインストールを行う事とし、その際に FLAVOR を set する。具体的には、
# FLAVOR='ap2' ; export FLAVOR
# make
# make install
/usr/local/lib/php-5.3/ にある libphp5.so を httpd.conf に登録
LoadModule php5_module /usr/local/lib/php5-3/libphp.so
AddType application/x-httpd-php .php .phtml

# apachectl2 start
#

上記のように、FLAVOR環境変数で ap2 (apache2オプションなんだろね) という設定を有効にしてから build する。
この後、php で書かれたコンテンツが正常に表示される事を確認した。メデタシ

参照: OpenBSD FAQ 8 - 一般的な質問

BSD系の寄り道

OpenBSDを使った自宅サーバーを構築中。
初めは NetBSD6.0 を使っていたが、何故か一晩でダウンする現象に見舞われ、OpenBSD5.2に入替え、現在は問題無し。
さらに、夜中のHDD音がうるさいので、32GBのSSD に交換し、無音サーバーになった。
※厳密には、ファン音が微かに聞こえるのだが、これは無視出来る程度。

OpenBSD はサーバー構築中に 5.2 がリリースされたので、snapshot からリリース版に速攻で移行した。

現在は apache2.22 が稼働中。ブログ機能を追加する為に、PHP5, MySQL, WordPress をインストール作業中。ところどころに落とし穴有り(ハマっている最中)。

2012年5月1日火曜日

gcc SH2クロス開発環境

arm Linux 構築用の環境も作成していかなければならないのだが、SH2Aモジュールの為のクロス開発環境も必要なので、ここにメモを残しておく。

開発環境は Windows上の Cygwin を使う。Ubuntu 等のLinuxネイティブ環境上で作るのも良いが、今使っているPC上には Cygwin しか動かしていないので、この環境をメインとする。

gcc 4.5.0
binutlis 2.20
ライブラリは newlib(1.18.0)
ターゲット:sh-hitachi-elf , v850-nec-elf の2種類
オプション:--disable-nls --enable-languages=c --with-gmp=/usr/local --enable-multilib --enable-interwork --with-newlib --with-headers=../newlib-1.18.0/newlib/libc/include

上記にあるように、gmp も Cygwin にインストールしてある。また、mpfr, mpc も同様。これらは gcc4以上を構築する場合は必須。

作成する方によっては、インストール先ディレクトリをターゲット単位に分けて PATH を追加している例も多いと思う。 が、手元の環境では --prefix=/usr/local で全部放り込んでいる。 1) Cygwin なので、環境構築に失敗したら、また最初から作り直せば良いと考えている 2) PATH を追加していく必要が無い というのがメリットか。

このような適当な環境構築であっても、CQ出版の雑誌付録の V850 / SH2A 基板で使うアプリケーションをビルドするには問題無い。

2011年8月26日金曜日

arm Linux 構築開始

筆者が務めている会社では、実は ARM ベースの ASIC を開発・販売していたりする。
ベースは arm926j なので、うまくやれば普通に Linux が動く(筈)。

で、会社の商品の中にも Linux 対応のソフトがあったりするのだが、これが Intel PCしか動作確認していないので、組込系 Linux でどうなるのか、実は良く判らない(爆)。

という訳で、どうせなら会社で作っている評価ボード(arm926jベース)に Linux 載せて動かしてしまおう、と思い付いた。

※実は評価ボード上で実際に Linux が既に動いているのだが、カーネルバージョンが古い。
で、上に書いた Linux 対応のソフトは、これよりも新しいカーネルでないと動かない。
なので、新しいカーネルで動かしてしまおう、というのが主旨。

対象とするのは、linux kernel 2.6.33.9。これにした理由は、rt パッチがあるから。
※筆者の会社は、工業用ネットワーク系製品を販売しているので、ターゲットはリアルタイム応答性を問うものが中心なので。

で、まずは開発環境の構築をしないといけない。
土台は Ubuntu(11.04)。ノートPC上で動いているので、ここで各種ツール類を構築する。
・binutils 2.21.1
・gcc 4.3.2
・glibc 2.13

Ubuntu 上での build の顛末について、書き足して行こうと考えている。
※実は上記の構成で、まで開発環境の構築が終わっていない(悲)。
どんでん返しが来るかも知れないが、少しずつ作っていこうと思う。

2009年5月16日土曜日

Linuxのモジュール化されたデバイスドライバ#2

さて、デバイスドライバをモジュールの形で作るには、何が必要になって来るか。

まず、Linux実行環境と同じバージョンの、Linuxソースコードが必要です。
ディストリビューションや組み込み機器環境によっては、オリジナルのコードにパッチを当てている事もあるかも知れません。
ソースコードのアーカイブの中には、/Documentation サブディレクトリがあって、この中にデバイスドライバを作る時のヒントになるようなファイルが数多くあります。公式な、最新のドキュメントですから、当たり前なんですけどね。

次に Makefile の記述に慣れた方が良いでしょう。
make コマンドは、カーネルを作る為の特別な拡張がされています。特別な拡張なので、一部の限られた本やWebなどのメディアしか登場してません。

そしてソースコード。デバイスドライバのソースコード記述の仕方も、いろいろと拡張されています。

さて、いろいろと並べるよりもソースコードを見た方が早いと思うので、以下にモジュール化されたデバイスドライバを示します。このドライバは、簡単なI/Oの読み書きをデバイスの read / write として見せるようになってます。実際に使う場合は、このデバイスを open した後、read あるいは write すれば良いです。


/*
* File: module-template.c
* Description: Small demo, how to write a Linux kernel module
*/
#include <linux/kernel.h> // for printk()
#include <linux/module.h> // this is module type device driver
#include <linux/moduleparam.h>
#include <linux/slab.h> // for kmalloc()...
#include <linux/errno.h> // use error codes
#include <linux/types.h> // size_t define
#include <linux/fcntl.h>
#include <linux/cdev.h> // this is character device
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/kdev_t.h>
#include <linux/ioport.h> // I/O drive
#include <asm/io.h> // I/O drive
#include <asm/uaccess.h> // user/kernel access API
#include <linux/ioctl.h> // needed for the _IOW etc stuff used later


インクルードファイルの定義で、普通の C プログラムと違う所に気が付いたでしょうか。
インクルードファイルのディレクトリ指定に linux/ とか asm/ とか付いてますね。
普段はこのようなディレクトリは指定しないと思います。

#define IOMAP_GPIO_BASE 0x00100800 // base for initialize
#define IOMAP_GPIO_RW_BASE 0x001008c8 // base for read/write
#define GPIO_CFG4 (0x00100810-IOMAP_GPIO_BASE)
#define GPIO_CFG5 (0x00100814-IOMAP_GPIO_BASE)
#define GPIO_CFG6 (0x00100818-IOMAP_GPIO_BASE)
#define GPIO_CFG7 (0x0010081c-IOMAP_GPIO_BASE)
#define GPIO_CFG9 (0x00100824-IOMAP_GPIO_BASE)
#define GPIO_OUT (0x001008c8-IOMAP_GPIO_BASE)
#define GPIO_IN (0x001008cc-IOMAP_GPIO_BASE)

ここは処理系や環境によって大幅に書き変わるでしょう。I/Oアドレスを定義しています。


/*
* module parameter
*/
static int major = 0; /* dynamic by default */
static void *pmap = 0; /* I/O base address, translated MMU */
module_param(major, int, 0);
module_param(pmap, int, 0);

モジュール内で使うパラメータの定義です。
上記の値は、このモジュール内でグローバル変数として使います。
あれ、再入可能なプログラムでグローバル変数は御法度では?上記の変数は、例え再入可能であっても唯一つの値を取ります。
デバイスドライバのメジャー番号と、I/Oアドレスの先頭を示すものですから。

但し今回のモジュールはモジュールをマイナー番号で複数定義したり、同時に複数の呼出しが来た時の処理が入ってません。
あくまでも、LinuxデバイスドライバでI/Oを簡単にアクセスする為の土台ですから。

MODULE_DESCRIPTION("Small module, self-learning only, not useful.");
MODULE_AUTHOR ("16min 10sec");
MODULE_LICENSE("Dual BSD/GPL");

上記のマクロのうち、大事なのは MODULE_LICENSE です。これが無いとカーネルをロードする際に警告が出ます。
BSDもしくはGPLライセンスで無いものは保証の限りでは無い、というものです。

/*
* open method : no operation, return zero all times
*/
int hello_open (struct inode *inode, struct file *fp)
{
// printk("hello:open\n");
pmap = ioremap_nocache(IOMAP_GPIO_RW_BASE, 8);
// printk("hello:pmap=%08x\n", pmap);
return 0;
}

オープン処理では、デバイスの物理アドレスを取得する事にします。
ここはデバイスドライバの初期化時に決めてしまっても構わないのですが、
ルールとして、このようにしてます。

/*
* release method : no operation, return zero all times
*/
int hello_release (struct inode *inode, struct file *fp)
{
// printk("hello:release\n");
iounmap(pmap);
return 0;
}

オープン処理でデバイスの物理アドレスを取得するので、リリース(close)時に
デバイスのアドレスをカーネルに返却してます。これで物理アドレスの取得と返却が
一対一で対応してますね。

/*
* read method
*/
ssize_t hello_read(struct file *fp, char __user *buf, size_t count, loff_t *f_pos)
{
unsigned int result;
char kbuff;
result = ioread32(pmap+4); // read address is stored pointer + 4
// printk("hello:read, count is %d, data is %08x\n", count, result);
kbuff = (char)(result & 0x0ff); // valid data is only 8 bits
copy_to_user((void *)buf, (const void *)&kbuff, 1);
// *buf = (char)(result & 0x0ff); // valid data is only 8 bits
return 1; // valid data size is 1
}

読み出し処理は2段階になってます。
最初に ioread32() で、物理アドレス(MMUで変換後のアドレス)からI/O値を読み取ってます。
次に、copy_to_user() を呼び出してます。名前で推測が付くと思いますが、これはデバイスドライバ
のあるカーネルから、ユーザのアプリケーション空間へのデータコピー関数です。

デバイスドライバはカーネルのメモリ空間に置かれてます。データメモリもカーネル全体で共通のもの
です。ですが、ユーザーからのリード・ライト時のバッファ空間は、ユーザ空間にあり、そのままではアクセス出来ません。

そこで、カーネル空間とユーザ空間との橋渡しを行うAPIを呼び出し、データの受け渡しを行います。
※全てのLinuxでユーザ空間がデバイスドライバが見えないとは限りません。
今回のテストで使用した Linux の場合、copy_to_user / copy_from_user 関数を呼ばなくても、データの受け渡しをする事は可能でした。
が、処理系に依存する記述は極力避けておきたいので、copy_to_user / copy_from_user 関数を挟んであります。

/*
* write method
*/
ssize_t hello_write(struct file *fp, char __user *buf, size_t count, loff_t *f_pos)
{
unsigned int data;
unsigned int outdata;
char kbuff;
// printk("hello:write, count is %d\n", count);
if(count) {
data = ioread32(pmap); // get current data from GPIO_OUT
// printk("hello:write, current data is %08x\n", data);
copy_from_user((void *)&kbuff, (const void *)buf, 1);
// printk("hello:write, from user, data is %02X\n", kbuff);
outdata = (*buf & 1);
outdata = outdata <<> bit9
outdata |= (data &= 0xfffffdff); // update
iowrite32(outdata, pmap); // store new data into GPIO_OUT
// data = ioread32(pmap); // get current data from GPIO_OUT
// printk("hello:write complete, wrote %08x, register %08x\n", outdata, data);
return 1;
} else {
return 0;
}
}

書き込み処理も2段階です。
まず最初に、ユーザー空間にある書き込みデータを copy_from_user 関数で取り出します。
次にiowrite32関数でI/O空間に書き込みます。
最後に、書きこんだバイト数を戻り値として返します。

/*
* ioctl method
*/
int hello_ioctl(struct inode *pnode, struct file *fp, unsigned int cmd, unsigned long arg)
{
// for implementation, please refer P.137 of LINUX Device Driver rev.3 book
// printk("hello:ioctl\n");
return 0;
}

IOCTL は現在何も処理関数を定義していませんが、ここにデバイス制御を行う為のAPIを用意したりする事が可能です。

/*
* register driver points
* open, read, write, ioctl, and close
*
*/
struct file_operations hello_fops = {
.owner = THIS_MODULE,
.read = hello_read,
.write = hello_write,
.ioctl = hello_ioctl,
.open = hello_open,
.release = hello_release,
};

この構造体は、デバイスドライバのうち、どのような機能を盛り込んでいるかをカーネルに教える為に重要です。
各処理関数名をここに記載します。

/*
* hello_init - function to insert this module into kernel space
*
* This is the first of two exported functions to handle inserting this
* code into a running kernel
*
* Returns 0 if successfull, otherwise -1
*/

static int __init hello_init(void)
{
int result;

printk("Hello World, here is your module speaking\n");

// request memory mapped I/O region
// GPIO4, 5, 6, 7 are as read (SW), and GPIO9 is as write (LED)
// there GPIO ports are located at 0x001008C8 (output), and 0x001008CC (input)
// note that these two registers are also located GPIO[15:0], are shared another purpose.
//

pmap = ioremap_nocache(IOMAP_GPIO_BASE, 256);
printk("hello: ioremap_nocache() is returned %x\n", pmap);

result = ioread32(pmap+GPIO_CFG9);
if(result != 0) { // already configured ?
printk("hello: GPIO_CFG9 is already configured %08x\n", result);
} else {
printk("hello: GPIO_CFG9 = %08x\n", result);
iowrite32(result | 0x11, pmap+GPIO_CFG9); // set MODE=10, affect GPIO_OUT
}

iounmap(pmap);

// finally, this driver is registered as character device
//
/*
* it is old style, but keep it.
*/
result = register_chrdev(major, "hello", &hello_fops);
printk("hello: tried to regist character device, result is %x\n", result);
if(result < 0) {
printk(KERN_INFO "hello: cannot get major number\n");
return result;
}
if(major == 0) major = result; /* get dynamic device number */
return 0;
}

デバイスドライバの初期処理をここに記述します。 まず、必要なI/Oアドレス空間をマッピングしています。この処理が正常終了しなかった場合、I/Oを制御する事は出来ません。 マッピングを行った後、I/Oの初期処理を行います。このデバイスドライバは評価用ですので、いくつかのI/Oアドレスの内容を読み取り、コンソールへ表示しています。 処理が終われば、unmap しても構いません。その代わり、このデバイスをオープンした際には、改めてI/O空間をマッピングする必要があります。 ※どうしてI/O空間をマッピングしたのに unmap しているかというと、このデバイスドライバが動く基板のI/Oアドレス空間は、他のデバイスが使う空間も一部含まれていて、他のドライバが後からこの空間の一部を使う可能性がある為です。 最後に、デバイスドライバのメジャー番号を登録します。この呼出しは古いスタイルなので、新しい方式に書き換えた方が良いかも知れません。

/*
* hello_cleanup - function to cleanup this module from kernel space *
* This is the second of two exported functions to handle cleanup this
* code from a running kernel
*/
static void __exit hello_cleanup(void) {
printk("Short life for a small module...\n");
unregister_chrdev(major, "hello");
}

モジュール化されたデバイスドライバを削除する時、具体的には rmmod コマンドが呼び出された時に、この関数が呼ばれます。 この時に、デバイスドライバ内部で獲得したメモリ空間の解放などを行う必要があります。 このドライバは、メモリ空間の獲得とかは行っていないので、最後のデバイス解放を行うだけで良いです。

module_init(hello_init);
module_exit(hello_cleanup);

最後に書いてある module_init と module_exit はマクロになっていて、デバイスドライバモジュールの登録・削除を行うのに必要な手続きが、自動的に追加されます。