繁体   English   中英

无法使用 uint64_t 指针通过 mmap 读取 pci csr

[英]Failed to read pci csr via mmap using uint64_t pointer

我正在尝试通过openmmap /dev/mem 在我的系统上读取 PCI CSR(配置空间寄存器)。

我在使用8字节长度读取时遇到了一些问题

这是我的代码的最小工作示例

#include <errno.h>
#include <fcntl.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define FATAL                                                                 \
    do {                                                                      \
        fprintf(stderr,                                                       \
                "Error at line %d, file %s (%d) [%s]\n",                      \
                __LINE__,                                                     \
                __FILE__,                                                     \
                errno,                                                        \
                strerror(errno));                                             \
        exit(1);                                                              \
    } while(0)
#define PAGE_SIZE 4096UL
#define PAGE_MASK (PAGE_SIZE - 1)

typedef struct rw_config rw_config;

struct rw_config {
    uint64_t address;
    uint64_t data;
};

static uint64_t _mmio_read_worker(uint64_t address) {
    int fd;
    void *map_base = NULL;
    void *map_address = NULL;
    uint64_t result = 0UL;

    if((fd = open("/dev/mem", O_RDONLY | O_SYNC)) < 0) FATAL;
    // PAGE_SIZE = 4096UL
    // PAGE_MASK = (PAGE_SIZE - 1) = 4095UL
    if((map_base = mmap(NULL,
                        PAGE_SIZE,
                        PROT_READ,
                        MAP_SHARED,
                        fd,
                        (address & ~PAGE_MASK)))
       == MAP_FAILED)
        FATAL;
    map_address = map_base + (address & PAGE_MASK);

    result = *(uint64_t *)map_address;
    printf("uint32_t 0x%016x, uint64_t 0x%016lx\n",
           (*(uint32_t *)map_address),
           (*(uint64_t *)map_address));

    close(fd);
    return result;
}

void rw_worker(rw_config *cfg) {
    cfg->data = _mmio_read_worker(cfg->address);
    return;
}

int main(int argc, char *argv[]) {
    rw_config *cfg = malloc(sizeof(rw_config));

    cfg->address = 0x80000000;
    cfg->data = 0x0;

    rw_worker(cfg);

    return 0;
}

读取address = 0x80000000 ,这是 pci mmio 基地址。

我的代码output如下:

uint32_t 0x0000000009a28086, uint64_t 0xffffffffffffffff

我尝试使用 gdb 来获取一些信息。

(gdb) printf "0x%llx\n",(*(uint64_t *)map_address)
0x10000009a28086
# before assigning 'result'
(gdb) printf "0x%llx\n",result
0x0
(gdb) next
# after assigning 'result'
(gdb) printf "0x%llx\n",result
0xffffffffffffffff
(gdb) print map_address
$2 = (void *) 0x7ffff7ffb000
(gdb) x/1xg 0x7ffff7ffb000
0x7ffff7ffb000: 0x0010000009a28086

我想我无法将(void*)转换为*(uint64_t *) ,但为什么呢?

map_address 中的值存储是正确的,我使用错误的方式获取值吗?

看了其他会员的回复,看了一些可能和这个bug相关的文档,以下是自己的一些感悟:

  • 我尝试用另一个不在 PCI CSR(配置空间寄存器)中的地址进行测试,并得到了正确的值。 所以我认为这个错误与硬件寄存器有关,而不是软件实现

  • 在EDK II UEFI Writer Guide 链接中,使用64bits read on PCI BAR(Base Address Register, PCI CSR的一部分)可能会导致alignment错误,你应该使用2x of 32bits read来实现64bits read。 虽然在示例中并没有强制要求整个 CSR 都有这个限制,但我认为这个错误已经有了很好的理由。

必须使用 1、2 或 4 字节访问来读取 PCIe 配置空间。 不允许进行 8 字节访问。 我的经验是 8 字节访问总是在所有字节中返回 FF。 这是 PCIe 规范要求的结果,即配置空间访问不能跨越 4 字节对齐的边界(第 7.2.2 节)。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM