首页
社区
课程
招聘
[结贴]DMA共用缓存区映射到用户进程空间失败,KERNEL_DATA_INPAGE_ERROR蓝屏
发表于: 2016-12-9 17:26 7577

[结贴]DMA共用缓存区映射到用户进程空间失败,KERNEL_DATA_INPAGE_ERROR蓝屏

2016-12-9 17:26
7577
小弟试图在驱动程序申请一段公用缓存区连续内存供DMA操作使用,然后在IO响应程序中将这个连续内存的首地址映射给用户程序访问,已降低数据搬移的时间消耗。

在内存开辟(申请)上没有问题,可以满足我的需要,但在映射超过一定大小的公用缓冲区的过程中就会蓝屏。

1.当DMA_SIZE约大于800k时MmMapLockedPagesSpecifyCache会导致蓝屏,当DMA_SIZE在800k以下基本可以正常映射.我也尝试过用
ExAllocatePoolWithTag 和 MmAllocateContiguousMemorySpecifyCache 来申请内存去映射到用户进程空间,也出现这个映射失败的蓝屏情况.
看到看雪中这篇帖子http://bbs.pediy.com/showthread.php?p=818239 中提到映射1M到16M都没有问题,我映射800K以上就蓝屏,不知道代码有没有什么问题,出错函数应该是
MmMapLockedPagesSpecifyCache,但我不清楚问题出在哪里~

2.目标及调试环境:win7 64bit RAM:4G
开发环境:win7 64bit VS2013 WDK8.1
3.驱动上下文结构体定义(Device.h)
  typedef struct _DEVICE_CONTEXT{
  
    struct  _tag_DrvMapApp_Reg{
      PHYSICAL_ADDRESS   DevBaseAddr;   //物理地址或者逻辑地址(设备访问需要)
      PVOID         DrvBaseAddr;   //内核态虚拟地址(驱动程序访问需要)
      PVOID         AppBaseAddr;   //用户态虚拟地址(应用程序访问需要)
      unsigned long int   length;
      int         isBusy;
      PMDL        pMdl;
      WDFDMAENABLER    dmaEnable;
      WDFCOMMONBUFFER    commonBuffer;
      WDFDMATRANSACTION  DmaTransaction;
      WDFINTERRUPT    Interrupt;
      WDFREQUEST      Request;
    }DrvMapAppReg1;
    ULONG              Counter_i;          // 资源计数器,记录WDF框架分配给设备的资源个数,counter for WdfCmResourceListGetCount

(ResourceListTranslated)
    ULONG        OffsetAddressFromApp;    // get offset address that is given by application 偏移地址(由应用程序传递过来)
  } DEVICE_CONTEXT, *PDEVICE_CONTEXT;
4.EvtDeviceAdd函数中
    #define DMA_SIZE (900*1024)

  PVOID            DrvUseAddr1 = NULL;
  PHYSICAL_ADDRESS      DevUseAddr1;
  WDF_DMA_ENABLER_CONFIG    dmaConfig; 
  DevUseAddr1.QuadPart  =  0;

  WdfDeviceSetAlignmentRequirement(device, FILE_OCTA_ALIGNMENT);  //16字节对齐
  //创建一个DMA适配器
  WDF_DMA_ENABLER_CONFIG_INIT(  &dmaConfig,
                  WdfDmaProfileScatterGather64Duplex,    //通道特性
                  DMA_SIZE/20                //单个传输的最大长度,小于65536

(64K)
                );
  status = WdfDmaEnablerCreate(  device,
                  &dmaConfig,
                  WDF_NO_OBJECT_ATTRIBUTES,
                  &pDevContext->DrvMapAppReg1.dmaEnable
                );
  if (!NT_SUCCESS(status)){
    return status;
  }

  //创建一个DMA传输
  status = WdfDmaTransactionCreate(  pDevContext->DrvMapAppReg1.dmaEnable,
                    WDF_NO_OBJECT_ATTRIBUTES,
                    &pDevContext->DrvMapAppReg1.DmaTransaction
                  );
  //分配DMA公用缓冲区
  status = WdfCommonBufferCreate(    pDevContext->DrvMapAppReg1.dmaEnable,
                    DMA_SIZE,
                    WDF_NO_OBJECT_ATTRIBUTES,
                    &pDevContext->DrvMapAppReg1.commonBuffer
                  );
  if (status ==  STATUS_SUCCESS){ //能够正常获取
    DrvUseAddr1 = WdfCommonBufferGetAlignedVirtualAddress(pDevContext->DrvMapAppReg1.commonBuffer);
    DevUseAddr1 = WdfCommonBufferGetAlignedLogicalAddress(pDevContext->DrvMapAppReg1.commonBuffer);
    pDevContext->DrvMapAppReg1.DrvBaseAddr = DrvUseAddr1;
    pDevContext->DrvMapAppReg1.DevBaseAddr = DevUseAddr1;
    pDevContext->DrvMapAppReg1.AppBaseAddr = NULL;
    pDevContext->DrvMapAppReg1.isBusy    =  0;
    pDevContext->DrvMapAppReg1.length    =  DMA_SIZE;
    RtlZeroMemory(pDevContext->DrvMapAppReg1.DrvBaseAddr, pDevContext->DrvMapAppReg1.length);
    KdrPrint("DMA_SIZE = %d KB", WdfCommonBufferGetLength(pDevContext->DrvMapAppReg1.commonBuffer) / 1024);
  }
  else{
    return status;
  }

5.在EvtIoDeviceControl例程中:
类型定义
typedef struct _tag_DMA_MEMORY{
  void * addr; //注意环境切换 win32 or x64  指针变量的长度不一样
  unsigned long  length;
}DMA_MEMORY_Header, *PDMA_MEMORY_Header;

    case PCIeCardDrv_IOCTL_GET_THREAD_ADDR:
    //outBuffer返回进程需要使用DMA空间的首地址和长度,在switch之前已经获取
      KdrPrint("EvtIoDeviceControl: I am GET_THREAD_ADDR!\n");
      MapSharedMemory2App(pDevContext, (PDMA_MEMORY_Header)outBuffer);
      WdfRequestCompleteWithInformation(Request, status, OutputBufferLength);
      if (!NT_SUCCESS(status)){
        goto Exit;
      }
    break;

6.其中MapSharedMemory2App的具体实现如下:
NTSTATUS MapSharedMemory2App(PDEVICE_CONTEXT pDevContext,PDMA_MEMORY_Header pMemoryHeader){

  PMDL        pMdl      =  NULL;
  PVOID        AppBaseAddr    =  NULL;
  pMemoryHeader->addr = NULL;
  pMemoryHeader->length = 0;
  
  
  
  pMdl = IoAllocateMdl(pDevContext->DrvMapAppReg1.DrvBaseAddr, DMA_SIZE, FALSE, FALSE, NULL);
  pMdl->MdlFlags |= MDL_MAPPED_TO_SYSTEM_VA; // 改变MDL的flags为可读状态
  MmBuildMdlForNonPagedPool(pMdl); //非页面缓冲池调用这个
  AppBaseAddr = MmMapLockedPagesSpecifyCache(pMdl,                  //
                        UserMode,
                        MmNonCached,
                        NULL,
                        FALSE,
                        NormalPagePriority// | MdlMappingNoWrite
                      );

  pDevContext->DrvMapAppReg1.AppBaseAddr    =  AppBaseAddr; //保存信息到
  pDevContext->DrvMapAppReg1.pMdl        =  pMdl;
  pDevContext->DrvMapAppReg1.length      =  DMA_SIZE;
  pMemoryHeader->addr              =  AppBaseAddr;
  pMemoryHeader->length            =  DMA_SIZE;
  KdrPrint("2:pMdl = 0x%x\n", pDevContext->DrvMapAppReg1.pMdl);

  KdrPrint("I am MapSharedMemory2App Return\n");
  return STATUS_SUCCESS;
}

希望各位能指点一下原因和怎么解决这个问题,谢谢~

[培训]内核驱动高级班,冲击BAT一流互联网大厂工作,每周日13:00-18:00直播授课

收藏
免费 0
支持
分享
最新回复 (4)
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
2
看这里
http://blog.sina.com.cn/s/blog_504675980102wbu7.html

你这个蓝屏是乱用
MmBuildMdlForNonPagedPool引发的
2016-12-9 21:02
0
雪    币: 36
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
3
感谢关注,可能我在帖子描述的不太清楚:
我的目的是用把申请到的内存首地址映射到用户空间去,如您链接中提到的方法,内存申请是没有问题的;
蓝屏问题是在映射的时候出现的,我按照您的思路屏蔽了MmBuildMdlForNonPagedPool发现情况还是一样,内存开辟没有问题,映射超过800K的内存空间到用户空间就会蓝屏。
2016-12-10 17:44
0
雪    币: 8835
活跃值: (2404)
能力值: ( LV12,RANK:760 )
在线值:
发帖
回帖
粉丝
4
Flags不要改!!!
先MmBuildMdlForNonPagedPool
后MmProbeAndLockPages
最后MmMapLockedPagesSpecifyCache
2016-12-10 18:58
0
雪    币: 36
活跃值: (17)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
5
按照您的提示我把代码改成了:
        pMdl = IoAllocateMdl(pDevContext->DMAReg.DrvBaseAddr, DMA_SIZE, FALSE, FALSE, NULL);
        MmBuildMdlForNonPagedPool(pMdl);
        try{
                MmProbeAndLockPages(pMdl, KernelMode, IoWriteAccess);//在内存中锁定,并且指明对它的改写权力
        }
        except(EXCEPTION_EXECUTE_HANDLER){
                KdPrint((" Exception during MmProbeAndLockPages"));
                IoFreeMdl(pMdl);
                return STATUS_UNSUCCESSFUL;
        }

        AppBaseAddr = MmMapLockedPagesSpecifyCache(pMdl,
                                        UserMode,
                        MmNonCached,
                        NULL,
                        FALSE,
                        NormalPagePriority
                        );

映射超过800K大小的公用缓存区蓝屏状况依旧,我还尝试增大系统的虚拟内存也没有效果,感谢关注,我准备使用缓存拷贝到应用层而不使用直接映射的方法了。
2016-12-12 17:10
0
游客
登录 | 注册 方可回帖
返回
//