Map a physical device to a pointer in user space

We have an embedded system in which a device with a memory display is connected, and the ARM processor is running Linux. The device is located at 0x40400000 and occupies megabytes (most of it is not supported by real memory, but the address space is mapped to the device in any case). We currently do not have a device driver for this device.

The device has a special read-only register (called CID) at 0x404f0704 . This register contains the value CID = 0x404 . I am trying to read this register from a program running on ARM.

Web Search I learned about the mmap() function, which supposedly allows me to access a physical address from user space. So, trying to find a couple of examples that I found, I wrote the following test:

 #include <sys/mman.h> #include <fcntl.h&gt #include <err.h&gt #include <stdio.h&gt #include <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); }
#include <sys/mman.h> #include <fcntl.h&gt #include <err.h&gt #include <stdio.h&gt #include <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); } 

Compiling with the ARM cross-compiler:

 a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c 

I can not get the expected result. I see that:

 pu = 0x40400000 pcid = 0x404f0704 CID = 0 

instead of the expected

 CID = 404 

What am I missing / doing wrong here?


UPDATE:

I found another demo program and executed its code. I managed to run my 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); }
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); } 

However, I'm not quite sure why the first version does not work. I realized that after using MAP_ANONYMOUS you do not need a file descriptor for matching. Also, I obviously mistakenly accept the addr argument ( pepi in my first version) as a physical address. If I am now, then this is actually a virtual address.

+6
source share
1 answer

Mmap is a function that usually works with virtual addresses. When you call mmap(... MAP_ANONYMOUS) (or the mmap the /dev/zero file), it will give you some new virtual memory filled with zero. The returned address will be the virtual memory address.

You can mmap some file (without MAP_ANONYMOUS), and then mmap will display the contents of the file in a certain range of virtual memory.

The device is located at 0x40400000

The MMIO device is in physical memory; any process can use the virtual address 0x40400000; but they will be displayed (translated) to some free physical page using the MMU (memory management unit). You cannot just ask the OS about some kind of virtual memory and expect it to be mmaped for a range of devices (this will be a variant of hell).

But there is a special device / dev / mem, which can be used as a file containing all physical memory. When you mmap / dev / mem, you really ask the OS to create a new mapping of some virtual memory in the requested physical range.

In your mmap call:

  mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, dev_base & ~MAP_MASK); 

you are requesting the mapping of the physical memory range [0x40400000 .. 0x4050000-1] (one megabyte, not including the 0x40500000 byte) to several megabytes of virtual memory (its starting address is returned by mmap).

+5
source

Source: https://habr.com/ru/post/927394/


All Articles