#define MMAP_MEM_PAGEALIGN (4*1024-1)
typedef struct
{
unsigned int memAddr;
unsigned int memSize;
unsigned int mmapMemAddr;
unsigned int mmapMemSize;
unsigned int memOffset;
int memDevFd;
volatile unsigned int *pMemVirtAddr;
} MMapCtrl;
MMapCtrl gMMapCtrl;
int32 common_MMap(uint32_t physAddr, uint32_t memSize , uint32_t *pMemVirtAddr){
gMMapCtrl.memDevFd = open("/dev/mem",O_RDWR|O_SYNC);
if(gMMapCtrl.memDevFd < 0)
{
printf(" ERROR: /dev/mem open failed !!!\n");
return -1;
}
gMMapCtrl.memOffset = physAddr & MMAP_MEM_PAGEALIGN;
gMMapCtrl.mmapMemAddr = physAddr - gMMapCtrl.memOffset;
gMMapCtrl.mmapMemSize = memSize + gMMapCtrl.memOffset;
gMMapCtrl.pMemVirtAddr = mmap(
(void*)gMMapCtrl.mmapMemAddr,
gMMapCtrl.mmapMemSize,
PROT_READ|PROT_WRITE|PROT_EXEC,MAP_SHARED,
gMMapCtrl.memDevFd,
gMMapCtrl.mmapMemAddr
);
if (gMMapCtrl.pMemVirtAddr==NULL)
{
printf(" ERROR: mmap() failed !!!\n");
return -1;
}
*pMemVirtAddr = (UInt32)((UInt32)gMMapCtrl.pMemVirtAddr + gMMapCtrl.memOffset);
return 0;
}
Int32 common_unmapMem()
{
if(gMMapCtrl.pMemVirtAddr)
munmap((void*)gMMapCtrl.pMemVirtAddr, gMMapCtrl.mmapMemSize);
if(gMMapCtrl.memDevFd >= 0)
close(gMMapCtrl.memDevFd);
return 0;
}
linux 內核為用戶提供了一個/dev/mem的驅動程序,使用戶直接訪問系統物理內存成為可能,上面的片段代碼就是利用mmap和/dev/mem建立起直接讀寫系統物理內存的渠道。搞過嵌入式開發的人應該熟悉上面的代碼,利用/dev/mem和mmap導出系統物理地址,免去了用戶虛擬地址到內核邏輯地址的繁瑣拷貝,提升效率。
1、簡單介紹下dev/mem
/dev/mem是linux下的一個字符設備,源文件是kernel/drivers/char/mem.c,有興趣的可以下載內核源碼看看,這個設備文件是專門用來讀寫物理地址用的。里面的內容是所有物理內存的地址以及內容信息。通常只有root用戶對其有讀寫權限。源引網絡資源對/dev/mem是這么評價的“/dev/mem是個好玩的東西,你竟然可以直接訪問物理內存,這在linux下簡直太神奇了,就想一個小偷想偷銀行,可是發現銀行戒備森嚴,正在小偷苦無對策的時候,突然發現銀行有個后門,而且這個后門直通銀行的金庫。”
2、mmap驅動實現時注意事項
曾經在寫某板卡的pcie驅動時,需要mmap出映射到pcie總線地址的對應的物理內存地址,代碼就不貼了,說下出的問題。在驅動加載上后,調用mmap出現了下面的錯誤,具體什么原因還是沒搞清楚,沒時間仔細研究了,工作不等人啊。
dspc868x_pcie_ep 0000:04:00.0: vma->vm_start: 0x4024a000
dspc868x_pcie_ep 0000:04:00.0: vma->vm_end: 0x4024b000
dspc868x_pcie_ep 0000:04:00.0: vma->vm_pgoff: 0x20000
dspc868x_pcie_ep 0000:04:00.0: Mapping 0x1000 bytes from address 0x20000000 success
Unhandled fault: Precise External Abort on non-linefetch (0x1018) at 0x4024a000
參考了別人寫的其他設備驅動程序,在我的驅動實現mmap代碼中添加如下的選項,禁止高速緩存選項,上面的錯誤就解決了。有對這部分特別熟悉的歡迎與我交流,指導下,謝謝。
vma->vm_flags |= VM_WRITE;
vma->vm_flags |= VM_RESERVED;
vma->vm_page_prot = (__pgprot(pgprot_val(pgprot_noncached(vma->vm_page_prot)) | (L_PTE_WRITE|L_PTE_DIRTY)));