C语言 使用uint64_t指针通过mmap读取pci csr失败



我正试图通过open,mmap/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%016lxn",
(*(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%llxn",(*(uint64_t *)map_address)
0x10000009a28086
# before assigning 'result'
(gdb) printf "0x%llxn",result
0x0
(gdb) next
# after assigning 'result'
(gdb) printf "0x%llxn",result
0xffffffffffffffff
(gdb) print map_address
$2 = (void *) 0x7ffff7ffb000
(gdb) x/1xg 0x7ffff7ffb000
0x7ffff7ffb000: 0x0010000009a28086

我猜我没有把(void*)铸造成*(uint64_t *),但为什么?

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

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

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

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

PCIe配置空间必须使用1、2或4字节访问读取。不允许8字节的访问

这是在PCIe规范中指定的,第2.2.7.1节:"对于配置请求,长度[9:0]必须是00 0000 01b."(长度在DW中指定)

我的经验是8字节访问总是返回所有字节的FF。

相关内容

  • 没有找到相关文章

最新更新