我们有一个嵌入式系统,其中连接了内存映射设备,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
在我的第一个版本中)误认为是物理地址。如果我现在是,那么这实际上是虚拟地址。