Access physical memory from Linux user space

If you want to find a way for access physical memory in Linux there are only two solutions. The first is to develop a module running in kernel space with the correct privileges to access physical memory and the second is to use a special devices called "/dev/mem". If your purpose is only to read or write some small parts of physical memory from user space this device is the right solution for you. Basically you can manage it as a file with additional use of some special functions required for get a pointer to the memory address your are interested to work in.

The first operation is to get handle of this special device, Please, note that in Linux system the access of this device is usually guarantee only to root user. If you want to get handle from a different user you need to change device access permission. Anyway the command for get the handle is the following:

int mem_dev = open("/dev/mem", O_RDWR | O_SYNC);

if(mem_dev == -1)
{
   // Error
}

and once finished obviously:

close(mem_dev);

Now the most important part. On Linux the physical memory pointer you want must be page aligned. This mean we need to change, in case, the original memory address you need to something the system can accept. The operation is quite simple and all you need are the following lines of code:

const uint32_t mem_address = 0x10001234
const uint32_t mem_size = 0x100;

uint32_t alloc_mem_size, page_mask, page_size;
void *mem_pointer, *virt_addr;

page_size = sysconf(_SC_PAGESIZE);
alloc_mem_size = (((mem_size / page_size) + 1) * page_size);
page_mask = (page_size - 1);

mem_pointer = mmap(NULL,
                   alloc_mem_size,
                   PROT_READ | PROT_WRITE,
                   MAP_SHARED,
                   mem_dev,
                   (mem_address & ~page_mask)
                   );

if(mem_pointer == MAP_FAILED)
{  
      // Error
}

virt_addr = (mem_pointer + (mem_address & page_mask)); 

At the end of this code, if no errors occurred, you have a pointer virt_addr to the physical memory space from address 0x10001234 to 0x10001334. You can use this pointer in read/write mode but be very carefully since an incorrect modify to some critical memory section will crash the system. Once finished to use the pointer you must free it as follow:

munmap(mem_pointer, alloc_mem_size);

Finish! Very short code and quite easy.

Comments

  1. first of all, excellent work!

    would classifying the memory address for a 64bit system change the variable type of the memory address to a uint64_t?

    ReplyDelete
  2. or maybe even simply use the size_t type which changes according to architecture?

    ReplyDelete
  3. Hi

    This example is for 32 bit system. In case you need to work on 64 bit system you have to change the variable type as you wrote but also use the correct mmap() call that in case of 64 bit is mmap64()

    ReplyDelete
  4. That's all fine and dandy, but try doing it within a device driver that you don't have access to an "open()" function.

    ReplyDelete
  5. It's exactly why I wrote on top of the post the following warning:

    "Please, note that in Linux system the access of this device is usually guarantee only to root user. If you want to get handle from a different user you need to change device access permission"

    ReplyDelete
  6. How much memory(I mean the max memory size) can be accessed by this method?

    ReplyDelete
  7. Sorry, I don't know for sure also because I think not all total memory can be accessed. Some memory block can be reserved and locked by the system.

    ReplyDelete
  8. I am trying to access pcie bar register from user space in linux-3.10 arm cortex-A15 architecture thro mmap function.While reading i am getting unhandled segmentation fault but write works (dont know about correctness). For your reference i am attaching code snippet

    pcie_drv_inst[dev_id].bar_info[0].virtAddr =

    (void *)mmap(NULL, pcie_drv_inst[dev_id].bar_info[0].length,

    (PROT_READ | PROT_WRITE),

    MAP_SHARED, pcie_drv_inst[dev_id].dev_desc,
    (off_t)pcie_drv_inst[dev_id].bar_info[0].pcieAddr);

    /* PCIe application registers virtual address (mapped to PCI window) */
    #define PCI_REGV(minor, reg) (unsigned *) (pcie_drv_inst[minor].bar_info[0].virtAddr
    + reg)

    bar_num =*(PCI_REGV(dsp_id, IB_BAR(ib_trans_num))) /*unhandled Segmentation fault
    occurs*/

    *(PCI_REGV(dsp_id, IB_BAR(ib_trans_num))) = bar_num; /* No error but dont know where
    it is writing !!!! */

    the above things works fine in x86 architecture. My question is anything has to be taken care in pcie base driver ?

    Note i am configuring 32-bit BARs.

    ReplyDelete
    Replies
    1. If you are trying to access a PCI device register memory this is not the way. In Linux you can access PCI registry using the /proc/bus/pci/devices path.

      Delete
    2. Just written a post regarding PCI access from user space here:

      http://falsinsoft.blogspot.com/2017/09/access-pci-device-registry-from-linux.html

      Delete
  9. Thanks so much! Works perfect for my project...

    ReplyDelete

Post a Comment

Popular posts from this blog

Access GPIO from Linux user space

Android: adb push and read-only file system error

Tree in SQL database: The Nested Set Model