简体   繁体   English

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

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

I'm trying to reading PCI CSR (Configuration Space Register) on my system via open , mmap /dev/mem.我正在尝试通过openmmap /dev/mem 在我的系统上读取 PCI CSR(配置空间寄存器)。

I met some problems when using 8 byte length reading我在使用8字节长度读取时遇到了一些问题

Here is the minimal working example of my code这是我的代码的最小工作示例

#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;
}

Reading the address = 0x80000000 which is pci mmio base address.读取address = 0x80000000 ,这是 pci mmio 基地址。

The output of my code is as follows:我的代码output如下:

uint32_t 0x0000000009a28086, uint64_t 0xffffffffffffffff

And I try to using gdb to get some information.我尝试使用 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

I guess I fail to casting (void*) to *(uint64_t *) , but why?我想我无法将(void*)转换为*(uint64_t *) ,但为什么呢?

The value storage in map_address is correct, am I using the wrong way to get the value? map_address 中的值存储是正确的,我使用错误的方式获取值吗?

After reading the replies from other members, I read some documents that may be related to this bug, and the following are some of my insights:看了其他会员的回复,看了一些可能和这个bug相关的文档,以下是自己的一些感悟:

  • I tried testing with another address which NOT in PCI CSR(Configuration Space Register), and got the correct value.我尝试用另一个不在 PCI CSR(配置空间寄存器)中的地址进行测试,并得到了正确的值。 So I think this bug is related to hardware registers rather than software implementation所以我认为这个错误与硬件寄存器有关,而不是软件实现

  • In EDK II UEFI Writer Guide link , using 64bits read on PCI BAR(Base Address Register, which is a part of PCI CSR) may cause an alignment fault, you should use 2x of 32bits read to achieve 64bits read.在EDK II UEFI Writer Guide 链接中,使用64bits read on PCI BAR(Base Address Register, PCI CSR的一部分)可能会导致alignment错误,你应该使用2x of 32bits read来实现64bits read。 Although in the example it is not enforced that the whole CSR has this limitation, but I think there is already a good reason for this bug.虽然在示例中并没有强制要求整个 CSR 都有这个限制,但我认为这个错误已经有了很好的理由。

PCIe config space must be read using 1, 2, or 4-byte accesses.必须使用 1、2 或 4 字节访问来读取 PCIe 配置空间。 8-byte accesses are not permitted.不允许进行 8 字节访问。 My experience is that 8-byte accesses always return FF in all bytes.我的经验是 8 字节访问总是在所有字节中返回 FF。 This is a result of the PCIe spec requirement that a config space access cannot cross a 4-byte-aligned boundary (section 7.2.2).这是 PCIe 规范要求的结果,即配置空间访问不能跨越 4 字节对齐的边界(第 7.2.2 节)。

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

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