カーネルが最初の構造体ページを格納するユーザー空間の物理メモリにアクセスしようとする単純なプログラムがあります。 64ビットマシンでは、このアドレスは次のとおりです。
ユーザー空間でmmapを使用してこの物理アドレスにアクセスしようとしています。しかし、次のコードはカーネルをクラッシュさせます。
int *addr;
if ((fd = open("/dev/mem", O_RDWR|O_SYNC)) < 0 ) {
printf("Error opening file. \n");
close(fd);
return (-1);
}
/* mmap. address of first struct page for 64 bit architectures
* is 0x0000620000000000.
*/
addr = (int *)mmap(0, num*STRUCT_PAGE_SIZE, PROT_READ, MAP_PRIVATE,
fd, 0x0000620000000000);
printf("addr: %p \n",addr);
printf("addr: %d \n",*addr); /* CRASH. */
私は問題を見つけたと思います-それはx86の/ dev/memメモリマッピング保護に関係しています。
PlはこのLWN記事を参照してください: "x86:設定オプションで/ dev/mem制限を導入してください" http://lwn.net/Articles/267427/
CONFIG_NONPROMISC_DEVMEM
現在(最近の3.2.21カーネルでこれをテストしました)、configオプションはCONFIG_STRICT_DEVMEMと呼ばれるようです。
カーネル構成を変更しました:
$ grep DEVMEM .config
# CONFIG_STRICT_DEVMEM is not set
$
上記のprgがpreviousカーネルでCONFIG_STRICT_DEVMEM SETを使用して実行された場合:dmesgは以下を示します:
[29537.565599] Program a.out tried to access /dev/mem between 1000000->1001000.
[29537.565663] a.out[13575]: segfault at ffffffff ip 080485bd sp bfb8d640 error 4 in a.out[8048000+1000]
これは、カーネル保護のためです。
カーネルが(CONFIG_STRICT_DEVMEM[〜#〜] unset [〜#〜]で)再構築され、上記のプログラムが実行されたとき:
# ./a.out
mmap failed: Invalid argument
#
これは、「オフセット」パラメーターが1 MBを超えるためです(x86では無効)(16 MBでした)。
Mmapオフセットを1 MB以内にした後:
# ./a.out
addr: 0xb7758000
*addr: 138293760
#
できます!詳細については、上記のLWNの記事を参照してください。
PATサポート(ページ属性テーブル)を備えたx86アーキテクチャーでも、カーネルはDRAM領域のマッピングを妨げます。 カーネルソース で言及されている理由は次のとおりです。
This check is nedded to avoid cache aliasing when PAT is enabled
このチェックにより、上記と同様のエラーが発生します。例えば:
Program a.out tried to access /dev/mem between [mem 68200000-68201000].
この制限は、PATを無効にすることで削除できます。ブート時にカーネルコマンドラインに「nopat」引数を追加すると、PATを無効にできます。
PATサポート(ページ属性テーブル)を備えたx86アーキテクチャーでは、カーネルはDRAM領域のマッピングを防止できます(CONFIG_NONPROMISC_DEVMEMを設定せずにコンパイルされた場合でも)。
カーネルソース で言及されている理由は次のとおりです。
This check is nedded to avoid cache aliasing when PAT is enabled
このチェックにより、上記のkaiwanの回答で述べたのと同様のエラーがdmesg
に表示されます。例えば:
Program a.out tried to access /dev/mem between [mem 68200000-68201000].
この制限は、PATを無効にすることで削除できます。
ブート時にカーネルコマンドラインにnopat
引数を追加することにより、PATを無効にできます。