title: Mapping a physical device to a pointer in User space
body: <p>We have an embedded system where a memory mapped device is connected, and an ARM CPU runs Linux. The device is located at address <code>0x40400000</code> and occupies a megabyte (most of it is not backed by an actual memory, but the address space is mapped to the device anyway). We currently <em>don't</em> have a device driver for this device.</p>
  
  <p>In the device there is a special read-only register (called CID) at address <code>0x404f0704</code>. This register contains the value <code>CID = 0x404</code>. I am trying to read this register from a program running on the ARM.</p>
  
  <p>Searching the net I learned about the <code>mmap()</code> function that supposedly lets me access a physical address from userspace. So, trying to follow a couple of examples I found, I wrote the following test:</p>
  
  <pre class="lang-c prettyprint-override">
  <code>
  #include &lt;sys/mman.h&gt;
  #include &lt;fcntl.h&gt
  #include &lt;err.h&gt
  #include &lt;stdio.h&gt
  #include &lt;stdlib.h&gt
  
  int main(void)
  {
      void          *pdev = (void *) 0x40400000;
      size_t         ldev = (1024*1024);
      int           *pu;
      int  volatile *pcid;
      int  volatile  cid;
  
      pu = mmap(pdev, ldev, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0);
      if (pu == MAP_FAILED)
          errx(1, "mmap failure");
  
      pcid = (int *) (((void *) pu) + 0xf0704);
  
      printf("pu    = %08p\n", pu);
      printf("pcid  = %08p\n", pcid);
  
      cid = *pcid;
      printf("CID   = %x\n", cid);
  
      munmap(pu, ldev);
  
      return (EXIT_SUCCESS);
  }
  </code>
  </pre>
  
  <p>Compiling with the ARM cross-compiler:</p>
  
  <pre><code>a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c
  </code></pre>
  
  <p>I can't get the expected result. What I see is that:</p>
  
  <pre><code>pu   = 0x40400000
  pcid = 0x404f0704
  CID  = 0
  </code></pre>
  
  <p>instead of the expected</p>
  
  <pre><code>CID  = 404
  </code></pre>
  
  <p>What am I missing / doing wrong here?</p>
  
  <hr>
  
  <p><strong>UPDATE:</strong></p>
  
  <p>I found another demo program and following its code I was able to get my code working:</p>
  
  <pre class="lang-c prettyprint-override">
  <code>
  int main(void)
  {
      off_t          dev_base = 0x40400000;
      size_t         ldev = (1024 * 1024);
      unsigned long  mask = (1024 * 1024)-1;
      int           *pu;
      void          *mapped_base;
      void          *mapped_dev_base;
      int  volatile *pcid;
      int  volatile  cid;
      int            memfd;
  
      memfd = open("/dev/mem", O_RDWR | O_SYNC);
      mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, dev_base & ~MAP_MASK);
      if (mapped_base == MAP_FAILED)
          errx(1, "mmap failure");
      mapped_dev_base = mapped_base + (dev_base & MAP_MASK);
      pu = mapped_dev_base;
  
      pcid = (int *) (((void *) pu) + 0xf0704);
  
      printf("pu    = %08p\n", pu);
      printf("pcid  = %08p\n", pcid);
  
      cid = *pcid;
      printf("CID   = %x\n", cid);
  
      munmap(mapped_base, ldev);
      close(memfd);
  
      return (EXIT_SUCCESS);
  }
  </code>
  </pre>
  
  <p>Still, I am not so sure why the 1st version did not work. My understanding was that once you use <code>MAP_ANONYMOUS</code> you do not need a file handle for the mapping. Also, I obviously mistaken the <em>addr</em> argument (<code>pepi</code> in my 1st version) to be the physical address. If I am right now, then this is actually the virtual address. </p>