【问题标题】:How to read /dev/mem using read()如何使用 read() 读取 /dev/mem
【发布时间】:2021-11-21 02:54:51
【问题描述】:

我正在尝试使用 /dev/mem 读取一些映射到 PCIe 设备的物理地址。 PCIe设备映射到0x387ffa000000

bash# lspci -s 1a:00.0 -v | grep Memory
      Memory at 387ffa000000 (64-bit, prefetchable) [size=32M]

所以我根据我在 SO 找到的内容编写了一些程序,它使用了mmap():

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s <phys_addr> <offset>\n", argv[0]);
        return 0;
    }

    off_t offset = strtoul(argv[1], NULL, 0);
    size_t len = strtoul(argv[2], NULL, 0);

    size_t pagesize = sysconf(_SC_PAGE_SIZE);
    off_t page_base = (offset / pagesize) * pagesize;
    off_t page_offset = offset - page_base;

    int fd = open("/dev/mem", O_SYNC);
    if (fd < 0) {
        perror("Can't open");
        return 1;
    }

    unsigned char *mem = mmap(NULL, page_offset + len, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, page_base);
    if (mem == MAP_FAILED) {
        perror("Can't map memory");
        return 1;
    }

    size_t i;
    for (i = 0; i < len; ++i)  printf("%02x ", (int)mem[page_offset + i]);
    printf("\n");

    return 0;
}

虽然这可行:

bash# ./mem_read.out 0x387ffa000000 0x10
00 1f 00 18 00 05 07 08 00 00 00 00 00 00 00 00

我只想尝试使用 read()/write() 调用而不是 mmap()。
但是当我尝试这个时:

int main(int argc, char *argv[]) {
    if (argc < 3) {
        printf("Usage: %s <phys_addr> <size>\n", argv[0]);
        return 1;
    }

    off_t offset = strtoul(argv[1], NULL, 0);
    size_t len   = strtoul(argv[2], NULL, 0);

    if (!len) {
        printf("size argument can't be 0\n");
        return 1;
    }

    int fd = open("/dev/mem", O_SYNC);
    if (fd < 0) {
        perror("Can't open");
        return 1;
    }

    if (lseek(fd, offset, SEEK_SET) < 0) {
        perror("Can't seek");
        return 1;
    }

    char* buff = (char*)malloc(len * sizeof(char) + 1);
    if (read(fd, buff, len) < 0) {
        perror("Can't read");
        return 1;
    }

    printf("%s\n", buff);

    return 0;
}

它失败了:

bash# ./mem_read.out 0x387ffa000000 0x10
Can't read: Bad address

当我使用常规文本文件而不是 /dev/mem 时,相同的代码可以工作。
有谁知道为什么前者有效而后者无效?

为了附加上下文,errno == EFAULT 时打印“错误地址”,read() 在以下情况下返回:

buf 在您可访问的地址空间之外。

这对我来说毫无意义。

【问题讨论】:

  • 请不要使用外部托管的图像来显示文本。复制/粘贴文本到问题中,我们可以看到它。

标签: c mmap pci-e


【解决方案1】:

查看/dev/mem 设备的代码,check 似乎该地址是一个有效的物理地址。

【讨论】:

  • 好的,我看到 valid_phys_addr_range() 检查它在评论中说:/* Can we access it for direct reading/writing? Must be RAM: */ 在我的情况下,地址没有映射到 RAM,而是映射到 I/O...跨度>
【解决方案2】:

您的open() 电话:

int fd = open("/dev/mem", O_SYNC);

the manual page 说:

参数标志必须包括以下访问权限之一 模式:O_RDONLY、O_WRONLY 或 O_RDWR。这些请求打开 文件分别为只读、只写或读/写。

所以我预计阅读甚至打开都会有问题。 可能O_RDONLY 是 0 值,我还没有检查过,但正式您的代码缺少该模式。

【讨论】:

  • O_RDONLY 确实是 0,所以这可能不是问题
猜你喜欢
  • 2021-02-14
  • 1970-01-01
  • 2018-03-16
  • 1970-01-01
  • 2011-09-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-10-25
相关资源
最近更新 更多