C语言 无法从内核模式读取 PCI BAR



我已经编写了一些代码,使用/sys 资源的 mmap 从用户空间探测 AMD Vega 的寄存器。这是有效的,我能够毫无问题地读取寄存器值。但是,当我实现此版本的内核模式版本时,所有读取都返回0xffffffff.我觉得我错过了一些简单的东西,因为用户空间代码返回了预期值,其中内核代码无法读取寄存器。

这是有效的用户空间代码:

#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <stdint.h>
#define RES_PATH "/sys/devices/pci0000:40/0000:40:01.3/0000:42:00.0/0000:43:00.0/0000:44:00.0/resource5"
#define RES_SIZE (512*1024)
#define MP0_BASE                     0x16000
#define mmMP0_SMN_C2PMSG_33          ((MP0_BASE + 0x61) * 4)
#define mmMP0_SMN_C2PMSG_64          ((MP0_BASE + 0x80) * 4)
#define mmMP0_SMN_C2PMSG_81          ((MP0_BASE + 0x91) * 4)
void * map;
uint32_t _preadl(const char * name, uint32_t reg)
{
  uint32_t val = *(uint32_t*)(map + reg);
  printf("readl %20s (0x%08x / 0x%08x) = 0x%08xn", name, reg / 4, reg, val);
  return val;
}
#define preadl(x) _preadl(#x, x)
int main(int argc, char * argv[])
{
  int fd = open(RES_PATH, O_RDWR | O_SYNC, 0);
  map = mmap(NULL, RES_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
  printf("mmap = %pn", map);
  preadl(mmMP0_SMN_C2PMSG_33);
  preadl(mmMP0_SMN_C2PMSG_64);
  preadl(mmMP0_SMN_C2PMSG_81);
  return 0;
}

这是不工作的内核代码:

static int reset_amdgpu_vega(struct pci_dev *dev, int probe)
{
#define MP0_BASE                        0x16000
#define mmMP0_SMN_C2PMSG_33             ((MP0_BASE + 0x61) * 4)
#define mmMP0_SMN_C2PMSG_64             ((MP0_BASE + 0x80) * 4)
#define mmMP0_SMN_C2PMSG_81             ((MP0_BASE + 0x91) * 4)
        resource_size_t mmio_base, mmio_size;
        void __iomem *mmio;
        int ret;
        int i;
        uint32_t val;
        if (probe)
                return 0;
        mmio_base = pci_resource_start(dev, 5);
        mmio_size = pci_resource_len(dev, 5);
        mmio = pci_iomap(dev, 5, 0);
        if (mmio == NULL) {
                printk(KERN_ERR
                       "[reset_amdgpu_vega] failed to map the resourcen");
                ret = -ENOMEM;
                goto out;
        }
        printk(KERN_INFO "mmio: %llx %llx %lxn",
               mmio_base,
               mmio_size,
               (uintptr_t)mmio);
        printk(KERN_INFO "regs: %08x %08x %08xn",
                mmMP0_SMN_C2PMSG_33,
                mmMP0_SMN_C2PMSG_64,
                mmMP0_SMN_C2PMSG_81);
        printk(KERN_INFO "vals: %08x %08x %08xn",
               ioread32(mmio + mmMP0_SMN_C2PMSG_33),
               ioread32(mmio + mmMP0_SMN_C2PMSG_64),
               ioread32(mmio + mmMP0_SMN_C2PMSG_81));
        pci_iounmap(dev, mmio);
        ret = 0;
out:
        return ret;
}

问题是在调用 pci 重置 quirk 之前,内核会关闭COMMAND寄存器上的 PCI_COMMAND_MEMORY 标志。这样,设备就不再可读。解决方案是先重新启用该标志:

 pci_read_config_word(dev, PCI_COMMAND, &cfg);
 cfg |= PCI_COMMAND_MEMORY;
 pci_write_config_word(dev, PCI_COMMAND, cfg);

最新更新