Linux下用memory方式访问PCIE空间

4 篇文章 2 订阅

测试环境:Ubuntu 14.04LTS

在Windows下,我们 用RW everything很容易可以看到PCIE所有的config space,但是我们最近想在Linux下dump PCIE config space,首先我们尝试用IO read的方式,

也就是通常我们会用CF8和CFC的方式,但是很遗憾这种方式只能读出来256个字节,那么后面的0x100~0x1FF怎么去读,就是下面我们要解决的问题。

1,首先我们需要获取PCIE在memory中的映射地址,我们看ACPI的手册,会发现有一个MCFG的东西,官方解释如下:

“MCFG” PCI Express memory mapped configuration space base address Description Table
PCI Firmware Specification, Revision 3.0
See the ACPI Link Document under the heading "PCI Sig".

也就是说MCFG里面有保存PCIE的基地址,这个的获取方式,可以利用acpidump这个工具,如果没有的话,需要先安装

sudo apt-get install acpidump

成功之后,我们键入acpidump,会dump出来很多register的值,慢慢下拉我们会找到如下的


从PCI Firmware Specification Revision 3.0上面可以看到,MCFG的定义:


看到失眠的Configuration space base address allocation structure,偏移地址是44,也就是0x2c,然后再参考Table4-3:


再回到acpidump读出来的MCFG,PCIE的基地址就是F0000000,这个值不是固定的,在其他的机子上会有不同。

2,为了验证上面的基地址就是PCIE的,我们需要写一个简单的程序,试试看。

justin@ubuntu:/mnt/hgfs/LinuxTools/PCITools/pci_dump$ sudo ./pci_dump -m
The offset address value is 0xf0000000
The value at 0xf0000000 is 0x71908086

好的,我们读出来memory address f0000000的低2个字节是8086,所以基地址肯定没有问题,之后就可以根据PCIE spec上的协议,组合PCIE 的Bus,Device,Function和Register的值来获取对应地址的值了。

如果不放心上面的验证,也可以通过lspci这个tool去读下bus 0, device 0,funtion 0,register 0的值,进一步验证下。






  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
好的,下面是一个简单的 wdf 驱动程序,可以访问 PCIe 配置空间。 ```c #include <ntddk.h> #include <wdf.h> DRIVER_INITIALIZE DriverEntry; EVT_WDF_DRIVER_UNLOAD OnDriverUnload; EVT_WDF_DRIVER_DEVICE_ADD OnDeviceAdd; EVT_WDF_OBJECT_CONTEXT_CLEANUP OnDeviceCleanup; typedef struct _DEVICE_CONTEXT { WDFDEVICE Device; } DEVICE_CONTEXT, *PDEVICE_CONTEXT; WDF_DECLARE_CONTEXT_TYPE_WITH_NAME(DEVICE_CONTEXT, GetDeviceContext); NTSTATUS DriverEntry(_In_ PDRIVER_OBJECT DriverObject, _In_ PUNICODE_STRING RegistryPath) { NTSTATUS status; WDF_DRIVER_CONFIG config; WDF_DRIVER_CONFIG_INIT(&config, OnDeviceAdd); config.EvtDriverUnload = OnDriverUnload; status = WdfDriverCreate(DriverObject, RegistryPath, WDF_NO_OBJECT_ATTRIBUTES, &config, WDF_NO_HANDLE); return status; } void OnDriverUnload(_In_ WDFDRIVER Driver) { UNREFERENCED_PARAMETER(Driver); } NTSTATUS OnDeviceAdd(_In_ WDFDRIVER Driver, _Inout_ PWDFDEVICE_INIT DeviceInit) { UNREFERENCED_PARAMETER(Driver); NTSTATUS status; WDF_OBJECT_ATTRIBUTES attributes; WDFDEVICE device; WDF_OBJECT_ATTRIBUTES_INIT_CONTEXT_TYPE(&attributes, DEVICE_CONTEXT); status = WdfDeviceCreate(&DeviceInit, &attributes, &device); if (!NT_SUCCESS(status)) { KdPrint(("Failed to create device (0x%08X)\n", status)); return status; } PDEVICE_CONTEXT context = GetDeviceContext(device); context->Device = device; return status; } void OnDeviceCleanup(_In_ WDFOBJECT Device) { UNREFERENCED_PARAMETER(Device); } NTSTATUS AccessPcieConfigSpace(_In_ WDFDEVICE Device) { NTSTATUS status = STATUS_SUCCESS; PDEVICE_CONTEXT context = GetDeviceContext(Device); // 获取设备对象的 PCI 对象 WDFDEVICE parentDevice = WdfPdoGetParent(Device); WDFMEMORY configSpaceMemory = NULL; ULONG configSpaceSize = 4096; // 分配内存来接收配置空间 status = WdfMemoryCreate( WDF_NO_OBJECT_ATTRIBUTES, NonPagedPoolNx, 0, configSpaceSize, &configSpaceMemory, NULL); if (!NT_SUCCESS(status)) { KdPrint(("Failed to allocate memory for config space (0x%08X)\n", status)); goto Cleanup; } // 读取配置空间 PHYSICAL_ADDRESS configSpaceBaseAddress = { 0 }; configSpaceBaseAddress.QuadPart = WdfDeviceGetBusData(parentDevice, PCI_WHICHSPACE_CONFIG, WdfPdoGetAddress(Device), WdfMemoryGetBuffer(configSpaceMemory, NULL), configSpaceSize); // 检查是否读取成功 if (configSpaceBaseAddress.QuadPart == 0) { KdPrint(("Failed to read config space\n")); status = STATUS_UNSUCCESSFUL; goto Cleanup; } // 输出配置空间 for (ULONG i = 0; i < configSpaceSize; i += 4) { ULONG value = *(PULONG)((PUCHAR)WdfMemoryGetBuffer(configSpaceMemory, NULL) + i); KdPrint(("Config space offset 0x%04X: 0x%08X\n", i, value)); } Cleanup: if (configSpaceMemory != NULL) { WdfObjectDelete(configSpaceMemory); } return status; } ``` 这个驱动程序的主要逻辑在 `AccessPcieConfigSpace` 函数中。它分配一个内存对象来接收 PCIe 配置空间,然后使用 `WdfDeviceGetBusData` 函数读取配置空间。读取到的配置空间存储在内存对象中,并可以使用 `WdfMemoryGetBuffer` 函数获取。 要在驱动程序中调用此函数,请在 `OnDeviceAdd` 函数的末尾添加以下代码: ```c status = AccessPcieConfigSpace(device); if (!NT_SUCCESS(status)) { KdPrint(("Failed to access PCIe config space (0x%08X)\n", status)); } ``` 当设备被添加时,此代码将调用 `AccessPcieConfigSpace` 函数,并打印出读取到的 PCIe 配置空间。请注意,此代码仅用于演示目的,您需要根据自己的需求修改它。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

技术在路上

帮助需要的人

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值