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

xoshrz7s  于 2023-01-08  发布在  其他
关注(0)|答案(2)|浏览(164)

我正在尝试通过openmmap/dev/mem 1阅读系统上的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基址。
我的代码的输出如下所示:

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中的值存储是正确的,我是否使用了错误的方法来获取值?

pdtvr36n

pdtvr36n1#

阅读了其他成员的回复后,我阅读了一些可能与此bug有关的文档,以下是我的一些见解:

  • 我试着用另一个不在PCI CSR(配置空间寄存器)中的地址进行测试,得到了正确的值。所以我认为这个错误与硬件寄存器有关,而不是软件实现
  • 在EDK II UEFI Writer Guide link中,在PCI BAR(Base Address Register,是PCI CSR的一部分)上使用64位读取可能会导致对齐错误,您应该使用32位读取的2倍来实现64位读取。虽然在示例中没有强制整个CSR具有此限制,但我认为此bug已经有了很好的理由。
5vf7fwbs

5vf7fwbs2#

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

相关问题