在用户空间中映射的物理设备的指针(Mapping a physical device to a po

2019-08-01 07:57发布

我们有一个嵌入式系统,其中一个存储器映射的装置连接,和一个ARM CPU运行Linux操作系统。 该装置位于地址0x40400000并且占用兆字节(大部分是不实际的存储器支持,但地址空间映射到该装置反正)。 我们目前没有此设备的设备驱动程序。

在该装置中有一个特殊的只读在地址寄存器(称为CID) 0x404f0704 。 该寄存器包含值CID = 0x404 。 我试图从在ARM上运行的程序读取该寄存器。

搜索网我了解了mmap()函数,理应让我从用户空间访问物理地址。 因此,试图跟随一对夫妇的例子,我发现,我写了下面的测试:


#include <sys/mman.h>
#include <fcntl.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>

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);
}

与ARM交叉编译器编译:

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

我不能得到预期的结果。 我看到的是:

pu   = 0x40400000
pcid = 0x404f0704
CID  = 0

而不是预期的

CID  = 404

我缺少/做错了什么?


更新:

我发现了另外一个演示程序,并按照它的代码我能得到我的代码工作:


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);
}

不过,我不是很确定为什么第一版本没有工作。 我的理解是,一旦你使用了MAP_ANONYMOUS你不需要对映射文件句柄。 另外,我明明走错了addr参数( pepi在我的第一个版本)是物理地址。 如果我是现在,那么这实际上是虚拟地址。

Answer 1:

MMAP是通常与虚拟地址的运作功能。 当你调用mmap(... MAP_ANONYMOUS)mmap/dev/zero文件),它会给你一个新的虚拟内存一定量,以0。 返回的地址将是虚拟内存地址。

你可以的mmap某些文件(不MAP_ANONYMOUS),然后将MMAP文件的内容映射到一些虚拟内存范围。

该装置位于地址0x40400000

设备MMIO位于物理内存; 任何过程可以使用虚拟地址0x40400000; 但他们将通过MMU(内存管理单元)映射(翻译)到一些免费的物理页。 你不能只问OS一些虚拟内存和期望是将mmaped到设备范围(这将是地狱的变体)。

但是有一个特殊的设备/ dev / MEM,这可以被用作包含所有物理存储器的文件。 当你mmap S /开发/ MEM你实际上是在问OS创建一些虚拟内存到物理问一系列新的映射。

在你的mmap调用:

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

你问映射物理存储器范围[0x40400000 .. 0x4050000-1]一兆字节;不包括字节0x40500000)转换成虚拟存储器(其起始地址由MMAP返回)的一些兆字节。



文章来源: Mapping a physical device to a pointer in User space