16

I have a simple program that tries to access the physical memory in user space, where the kernel stores the 1st struct page. On a 64 bit machine this address is:

  • kernel virtual address: ffffea0000000000
  • physical address: 0000620000000000

I am trying to access this physical address through mmap in user space. But the following code crashes the kernel.

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 Answers 2

21

I think I've found the issue -- it's to do with /dev/mem memory mapping protection on the x86.

Pl refer to this LWN article: "x86: introduce /dev/mem restrictions with a config option" http://lwn.net/Articles/267427/

CONFIG_NONPROMISC_DEVMEM

Now (i tested this on a recent 3.2.21 kernel), the config option seems to be called CONFIG_STRICT_DEVMEM.

I changed my kernel config:

$ grep DEVMEM .config
# CONFIG_STRICT_DEVMEM is not set
$ 

When the above prg was run with the previous kernel, with CONFIG_STRICT_DEVMEM SET: dmesg shows:

[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]

This is because of the kernel protection..

When the kernel was rebuilt (with the CONFIG_STRICT_DEVMEM UNSET) and the above prg was run :

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

This is because the 'offset' parameter is > 1 MB (invalid on x86) (it was 16MB).

After making the mmap offset to be within 1 MB:

# ./a.out 
addr: 0xb7758000
*addr: 138293760 
# 

It works! See the above LWN article for details.

On x86 architectures with PAT support (Page Attribute Table), the kernel still prevents the mapping of DRAM regions. The reason for this as mentioned in the kernel source is:

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

This check will cause a similar error to the one mentioned above. For example:

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

This restriction can be removed by disabling PAT. PAT can be disabled by adding the "nopat" argument to the kernel command line at boot time.

8
  • Hi Kaiwan, Thanks for pointing out the interesting config variable. 'CONFIG_STRICT_DEVMEM is not set' in my case. Kernel (3.4.6 & 3.1.0). After making the offset change the program works. But I was interested to access that address since it holds first struct page. is this possible?
    – Vinay
    Commented Aug 10, 2012 at 18:54
  • Also I set offset to 0x0000000000000000 and I get valid return address. But if I set the offset to some random address say 0x00000000000000ff, I don't get back a valid address. Do I have to set the address on page boundary?
    – Vinay
    Commented Aug 10, 2012 at 19:14
  • 1
    ARM requires using a page boundary for mmap(). From a little testing on IA32 it seems to be the case there as well...(& you've tried on the x86_64 i presume). Also, wrt your comment on accessing the first struct page, with CONFIG_STRICT_DEVMEM Off, i thought it would work (on a page boundary).. not sure about this..
    – kaiwan
    Commented Aug 12, 2012 at 3:11
  • @Vinay, You can disable checking. For example, always make devmem_is_allowed return 1. I know it's a hack. ;)
    – chenwj
    Commented Sep 6, 2012 at 8:13
  • 2
    @trblnc Thanks! your implementation seems simple- to mmap a particular hard-coded va. I maintain a more generalized software that can be used to gain r/w access to any memory (MMIO/registers/RAM) fr usermode; pl check out: github.com/kaiwan/device-memory-readwrite
    – kaiwan
    Commented Feb 16, 2017 at 12:37
4

On x86 architectures with PAT support (Page Attribute Table), the kernel can prevent the mapping of DRAM regions (even if it is compiled without setting CONFIG_NONPROMISC_DEVMEM).

The reason for this as mentioned in the kernel source is:

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

This check will cause a similar error to appear in dmesg as the one mentioned in kaiwan's answer above above. For example:

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

This restriction can be removed by disabling PAT.

PAT can be disabled by adding the nopat argument to the kernel command-line at boot time.

3
  • This (correct!) answer appears to have been copy pasted on the above answer by an anonymous user 8 mins after you answered. If you want that removed, let me know and I will edit it out, as I consider it unfair if done without your consent. Commented Jul 16, 2017 at 6:25
  • It was me. I was the one who added it to the previous answer and then added it as a separate answer because (apparently) I don't know how to use the tool :D. Commented Jul 17, 2017 at 12:15
  • OK! Let me know if you want it removed from the above answer. Cheers. Commented Jul 17, 2017 at 12:38

Not the answer you're looking for? Browse other questions tagged or ask your own question.