16

我有一个简单的程序,它试图访问用户空间中的物理内存,内核存储第一个结构页面。在 64 位机器上,这个地址是:

  • 内核虚拟地址:ffffea0000000000
  • 物理地址:0000620000000000

我正在尝试通过用户空间中的 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. */
4

2 回答 2

21

我想我找到了问题——这与 x86 上的 /dev/mem 内存映射保护有关。

请参考这篇 LWN 文章:“x86:使用配置选项引入 /dev/mem 限制” http://lwn.net/Articles/267427/

CONFIG_NONPROMISC_DEVMEM

现在(我在最近的 3.2.21 内核上对此进行了测试),配置选项似乎被称为 CONFIG_STRICT_DEVMEM。

我改变了我的内核配置:

$ grep DEVMEM .config
# CONFIG_STRICT_DEVMEM is not set
$ 

当上述 prg 使用以前的内核运行时,使用 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)并运行上述 prg 时:

# ./a.out 
mmap failed: Invalid argument
# 

这是因为 'offset' 参数 > 1 MB(在 x86 上无效)(它是 16MB)。

使 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。

于 2012-08-10T08:27:07.633 回答
3

在支持 PAT(页面属性表)的 x86 架构上,内核可以阻止 DRAM 区域的映射(即使它是在没有设置 CONFIG_NONPROMISC_DEVMEM 的情况下编译的)。

内核源代码中提到的原因是:

This check is nedded to avoid cache aliasing when PAT is enabled

此检查将导致出现与dmesg上述kaiwan 回答中提到的类似的错误。例如:

Program a.out tried to access /dev/mem between [mem 68200000-68201000].

可以通过禁用 PAT 来消除此限制。

nopat可以通过在引导时将参数添加到内核命令行来禁用 PAT 。

于 2016-04-14T21:34:35.300 回答