C语言 如何使用read()读取/dev/dev

pkln4tw6  于 11个月前  发布在  其他
关注(0)|答案(3)|浏览(148)

我正在尝试使用/dev/mem读取Map到PCIe设备的物理地址。PCIe设备Map到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时打印“Bad address”,当read()返回时:
buf在您可访问的地址空间之外。
这对我来说毫无意义

z2acfund

z2acfund1#

查看/dev/mem设备的代码,它似乎检查地址是否是有效的物理地址。

e5nqia27

e5nqia272#

读取驱动程序中的函数回调检查有效地址,如果地址无效,则返回EFAULT
正如在内核中添加此函数valid_phys_addr_range()时提交消息中所述。
x86没有定义valid_HAS_VALID_PHYS_ADDR_RANGE,这导致/dev/PHP使用drivers/char/mem.c中的默认valid_phys_addr_range()和valid_mmap_phys_addr_range()。
默认的valid_phys_addr_range()允许任何低于__pa(high_memory)的范围,这是系统RAM的结尾,不允许任何高于它的范围。
read()在其他架构上的表现如何,我不知道。

ffscu2ro

ffscu2ro3#

在/dev/css驱动程序中读取syscall回调,检查有效物理地址是否低于valid_phys_addr_range()中的high_memory,如果地址无效,则返回errno EFAULT。

相关问题