在我的C++程序中(在Windows上),我正在分配一块内存,并确保它在物理内存中保持锁定(未连接和连续)(即使用VirtualAllocEx(),MapUserPhysicalPages()等).
在我的过程中,我可以获得该块的VIRTUAL内存地址, 但我需要找出它的PHYSICAL内存地址,以便将其传递给某个外部设备.
1.在USER模式下,有什么方法可以将虚拟地址转换为程序中的物理地址吗?
2.如果没有,我只能在KERNEL模式下找到这个虚拟到物理映射.我想这意味着我必须写一个驱动程序才能做到这一点......?你知道我可以使用的任何现成的驱动程序/ DLL/API,我的应用程序(程序)将与之交互以进行翻译吗?
3.如果我必须自己编写驱动程序,我该如何进行翻译?我使用哪些功能?是mmGetPhysicalAddress()吗?我该如何使用它?
4.此外,如果我理解正确,mmGetPhysicalAddress()将返回调用进程上下文中的虚拟基址的物理地址.但是如果调用进程是驱动程序,并且我正在使用我的应用程序来调用该函数的驱动程序,那么我正在改变上下文,当调用mmGetPhysicalAddress例程时,我不再处于应用程序的上下文中...所以如何翻译应用程序(用户模式)内存空间中的虚拟地址,而不是驱动程序?
任何答案,提示和代码摘录将不胜感激!
谢谢
在我的C++程序中(在Windows上),我正在分配一块内存,并确保它在物理内存中保持锁定(未连接和连续)(即使用VirtualAllocEx(),MapUserPhysicalPages()等).
不,你无法确保它保持锁定状态.如果您的流程崩溃或提前退出怎么办?如果用户杀了它怎么办?该内存将被重用于其他内容,如果您的设备仍在执行DMA,最终将导致数据丢失/损坏或错误检查(BSOD).
此外,它MapUserPhysicalPages
是Windows AWE(Address Windowing Extensions)的一部分,用于在32位版本的Windows Server上处理超过4 GB的RAM.我不认为它是用于破解用户模式DMA.
1.在USER模式下,有什么方法可以将虚拟地址转换为程序中的物理地址吗?
有些驱动程序允许您执行此操作,但您无法在Windows上从用户模式编程DMA,并且仍然具有稳定且安全的系统.让作为受限用户帐户运行的进程读/写物理内存允许该进程拥有系统.如果这是一次性系统或原型,这可能是可以接受的,但如果您希望其他人(特别是付费客户)使用您的软件和您的设备,您应该编写驱动程序.
2.如果没有,我只能在KERNEL模式下找到这个虚拟到物理映射.我想这意味着我必须写一个驱动程序才能做到这一点......?
这是解决此问题的推荐方法.
你知道我可以使用的任何现成的驱动程序/ DLL/API,我的应用程序(程序)将与之交互以进行翻译吗?
您可以使用MDL(内存描述符列表)锁定任意内存,包括用户模式进程拥有的内存缓冲区,并将其虚拟地址转换为物理地址.您还可以让Windows DeviceIoControl
通过使用METHOD_IN_DIRECT
或临时为传递给调用的缓冲区创建MDL METHOD_OUT_DIRECT
.
请注意,虚拟地址空间中的连续页面在物理地址空间中几乎不会连续.希望您的设备能够处理这个问题.
3.如果我必须自己编写驱动程序,我该如何进行翻译?我使用哪些功能?是mmGetPhysicalAddress()吗?我该如何使用它?
编写驱动程序要比调用一些API要多得多.如果您要编写驱动程序,我建议您从MSDN和OSR中尽可能多地阅读相关材料.另外,请查看Windows驱动程序工具包中的示例.
4.另外,如果我理解正确,mmGetPhysicalAddress()将返回调用进程上下文中的虚拟基址的物理地址.但是如果调用进程是驱动程序,并且我正在使用我的应用程序来调用该函数的驱动程序,那么我正在改变上下文,当调用mmGetPhysicalAddress例程时,我不再处于应用程序的上下文中...所以如何翻译应用程序(用户模式)内存空间中的虚拟地址,而不是驱动程序?
驱动程序不是进程.驱动程序可以在任何进程的上下文中运行,也可以在各种提升的上下文(中断处理程序和DPC)中运行.
1)没有
2)是的,你必须写一个驱动程序.Best可以是虚拟驱动程序,也可以更改特殊外部设备的驱动程序.
3)这在这里变得非常混乱.MmGetPhysicalAddress
应该是您正在寻找的方法,但我真的不知道物理地址如何映射到bank/chip/etc. 在物理记忆上.
4)你不能使用分页内存,因为它被重新定位.您可以MmProbeAndLockPages
在MDL上锁定分页内存,您可以在从用户模式调用上下文传入的内存上构建.但最好分配非分页内存并将其交给用户模式应用程序.
PVOID p = ExAllocatePoolWithTag( NonPagedPool, POOL_TAG ); PHYSICAL_ADDRESS realAddr = MmGetPhysicalAddress( p ); // use realAddr
您的应用程序中有一个虚拟缓冲区.正如您所指出的那样,该范围的虚拟内存仅在您的应用程序的上下文中可用,并且其中一些可能随时被分页.因此,为了从设备访问内存(也就是说,执行DMA),您需要将其锁定并获取可以传递给设备的描述.
通过使用METHOD_IN_DIRECT或METHOD_OUT_DIRECT将IOCTL(通过DeviceControl函数)发送到驱动程序,可以获得称为MDL或内存描述符列表的缓冲区的描述.有关定义IOCTL的讨论,请参见以下页面.
http://msdn.microsoft.com/en-us/library/ms795909.aspx
现在您已在设备的驱动程序中描述了缓冲区,您可以将其锁定,以便缓冲区在设备可能对其执行的整个时间段内保留在内存中.在MSDN上查找MmProbeAndLockPages.
您的设备可能会也可能无法读取或写入缓冲区中的所有内存.该设备可能只支持32位DMA,机器可能有超过4GB的RAM.或者您可能正在处理具有IOMMU,GART或其他地址转换技术的计算机.为了适应这种情况,请使用各种DMA API来获取一组适合您的设备使用的逻辑地址.在许多情况下,这些逻辑地址将等同于您提出的问题的物理地址,但并非总是如此.
您使用哪种DMA API取决于您的设备是否可以处理分散/收集列表等.您的驱动程序在其设置代码中将调用IoGetDmaAdapter并使用它返回的一些函数.
通常,您会对GetScatterGatherList和PutScatterGatherList感兴趣.您提供了一个函数(ExecutionRoutine),它实际上对您的硬件进行编程以进行传输.
涉及很多细节.祝好运.
您无法从用户空间访问页表,它们将映射到内核中.
如果您在内核中,只需检查CR3的值以找到基页表地址,然后开始解析.
这个博客系列有一个如何做到这一点的精彩解释.您不需要任何OS工具/ API来解析虚拟< - >物理地址.
虚拟地址:f9a10054
1: kd> .formats 0xf9a10054 Binary: 11111001 10100001 00000000 01010100 Page Directory Pointer Index(PDPI) 11 Index into第1个表(页面目录指针表)页目录索引(PDI)
111001 101索引到第2个表(页面目录表)页表索引(PTI)
00001 0000索引到第3个表(页表)字节索引
0000 01010100 0x054,偏移到物理内存页面
在他的例子中,他们使用windbg,!dq是物理内存读取.