LKMからのファイル出力


LKMでの出力はprinkにてカーネルログとして出力させます。printkはメッセージをリングバッファに設定するだけです。カーネルのログの実際の表示先デバイスは、prinkのタイミングでは決定しようがありません。でないと、ユーザはどのコンソールからそのメッセージを見ていいのか悩んでしまうことになってしまいます。ユーザはsyslogd(dmesage等)で、指定するコンソールから確認できるわけです。

で、知識を深めると言う興味的好奇心から、このメッセージをカレント端末に表示できないかと思いました。出力先を/dev/tty0にすれば、HELLOW LKM WORLDのメッセーをカレント端末に表示してくれます。

で、/dev/tty0をオープンして、その書き込みコールバック関数を呼べばいいだけです。当初下位レベルの関数を駆使してとやってみましたが、LKMからシンボルが参照できないものが、多々あって残念しました。

そこで、直接システムコールを呼ぶことにいたしました。X86の場合、sys_open()は、結果をユーザ空間への橋渡しをする処理が介在して、うまくいきませんでした。で、サンプルではopenシステムコールから呼ばれるdo_sys_open()をコールする事にいたしました。

do_sys_open()はLKMにシンボルを公開していません。従って/boot/配下のシンボルマップから、直接この関数をアドレス(システム依存)をハードコピーいたしました。ここで大切なのは、この第二引数がconst char __user*となっている点です。filenameはユーザ空間のアドレスでないといけません。do_sys_open()内で、getname()でユーザ空間のfilenameを、カーネル空間に取り込むのですが、その時filenameがユーザ空間かどうかチェックしています。
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
とりあえず、カレントプロセスのメモリー空間から、do_mmap_pgoff()で空きメモリーリージョンを取得し、そこにまず、/dev/tty0を、複写して、do_sys_open()をコールします。

次に、それで取得した、ファイルIDから、fget()でFILE構造体を取得し、ファイルオペレーションをwriteコールバック関数をコールする事で実現しました。なお、ファイルオペレーションをwriteコールバック関数の、第二引数もユーザ空間である必要があります。先のdo_mmap_pgoff()で取得したアドレスを代用して、その出力メッセージをセットしています。
#include <linux/init.h>
#include <linux/module.h>
#include <linux/namei.h>
#include <linux/kernel.h>
#include <linux/mm.h>
#include <linux/file.h>
#include <linux/string.h>
#include <linux/fs.h>

#define PROT_READ       0x1     /* page can be read */
#define PROT_WRITE      0x2     /* page can be written */
#define MAP_PRIVATE     0x02    /* Changes are private */

MODULE_DESCRIPTION("syscall sample");
MODULE_AUTHOR("y.kitamura");
MODULE_LICENSE("GPL");

static int test_init(void)
{
   long (*my_do_sys_open)(int dfd, const char __user *filename, int flags, int mode)
                                                                   =(void *)0xc04da3d9;
   long             addr, fd, pos, ret;
   struct file     *file;

   addr = 1 << PAGE_SHIFT;
   addr = do_mmap_pgoff(NULL, addr, 4012,  PROT_READ | PROT_WRITE,
               MAP_PRIVATE, 0 >> PAGE_SHIFT);
   strcpy((char *)addr, "/dev/tty0");

   fd = (*my_do_sys_open)(AT_FDCWD, (char *)addr, O_RDWR, 0);
   if (fd > 0) {
       file = fget(fd);
       pos = file->f_pos;
       strcpy((char *)addr, "HELLOW LKM WORLD!!\n");
       ret = file->f_op->write(file, (char *)addr, strlen((char *)addr), &pos);            
       printk("write %d\n", ret);
   }
   else {
       printk("open err\n");
   }
   return 0;
}

static void test_exit(void)
{
}

module_init(test_init);
module_exit(test_exit);

補足

ここでのカレントプロセスはinsmodだと思います。取得したFILE構造体とかメモリーリージョンは、プロセスが終了すると解放してくれるので、その後処理はやっていません。他のLKMからも関数として利用するには、この関数をEXPORTして、出力後毎にメモリーリージョンとFILE構造体を解放すればいいように思いますが・・・。

とりあえず動作すると言うことで・・・。結構苦労しました。ハイ。

最終更新 2012/01/29 19:43:58 - north
(2012/01/29 19:24:34 作成)
添付ファイル
syscall.jpg - north


検索

アクセス数
3569922
最近のコメント
コアダンプファイル - sakaia
list_head構造体 - yocto_no_yomikata
勧告ロックと強制ロック - wataash
LKMからのファイル出力 - 重松 宏昌
kprobe - ななし
ksetの実装 - スーパーコピー
カーネルスレッドとは - ノース
カーネルスレッドとは - nbyst
asmlinkageってなに? - ノース
asmlinkageってなに? - よろしく
Adsense
広告情報が設定されていません。