6

我們有一個嵌入式系統,其中連接了內存映射設備,ARM CPU運行Linux。該設備位於地址0x40400000並佔用兆字節(大部分內容不由實際內存支持,但地址空間無論如何映射到設備)。我們目前有不需要有這個設備的設備驅動程序。將物理設備映射到用戶空間中的指針

該設備在地址0x404f0704有一個特殊的只讀寄存器(稱爲CID)。該寄存器包含值CID = 0x404。我正嘗試從運行在ARM上的程序讀取此寄存器。

搜索網絡我學到了mmap()函數,該函數可以讓我從用戶空間訪問物理地址。因此,試圖跟隨一對夫婦的例子,我發現,我寫了下面的測試:

 

#include <sys/mman.h> 
#include <fcntl.h> 
#include <err.h> 
#include <stdio.h> 
#include <stdlib.h> 

int main(void) 
{ 
    void   *pdev = (void *) 0x40400000; 
    size_t   ldev = (1024*1024); 
    int   *pu; 
    int volatile *pcid; 
    int volatile cid; 

    pu = mmap(pdev, ldev, PROT_READ|PROT_WRITE, MAP_ANONYMOUS|MAP_PRIVATE, -1, 0); 
    if (pu == MAP_FAILED) 
     errx(1, "mmap failure"); 

    pcid = (int *) (((void *) pu) + 0xf0704); 

    printf("pu = %08p\n", pu); 
    printf("pcid = %08p\n", pcid); 

    cid = *pcid; 
    printf("CID = %x\n", cid); 

    munmap(pu, ldev); 

    return (EXIT_SUCCESS); 
} 
 

與ARM交叉編譯器編譯:

a-gcc -O0 -g3 -o mmap-test.elf mmap-test.c 

我不能得到預期的結果。我看到的是:

pu = 0x40400000 
pcid = 0x404f0704 
CID = 0 

,而不是預期

CID = 404 

我缺少/做錯了什麼?


UPDATE:

我發現了另外一個演示程序和下面的代碼,我能得到我的代碼工作:

 

int main(void) 
{ 
    off_t   dev_base = 0x40400000; 
    size_t   ldev = (1024 * 1024); 
    unsigned long mask = (1024 * 1024)-1; 
    int   *pu; 
    void   *mapped_base; 
    void   *mapped_dev_base; 
    int volatile *pcid; 
    int volatile cid; 
    int   memfd; 

    memfd = open("/dev/mem", O_RDWR | O_SYNC); 
    mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, MAP_SHARED, memfd, dev_base & ~MAP_MASK); 
    if (mapped_base == MAP_FAILED) 
     errx(1, "mmap failure"); 
    mapped_dev_base = mapped_base + (dev_base & MAP_MASK); 
    pu = mapped_dev_base; 

    pcid = (int *) (((void *) pu) + 0xf0704); 

    printf("pu = %08p\n", pu); 
    printf("pcid = %08p\n", pcid); 

    cid = *pcid; 
    printf("CID = %x\n", cid); 

    munmap(mapped_base, ldev); 
    close(memfd); 

    return (EXIT_SUCCESS); 
} 
 

不過,我不太知道爲什麼第一次版本不起作用。我的理解是,一旦你使用MAP_ANONYMOUS你不需要一個文件句柄的映射。另外,我明顯錯誤地將addr參數(我的第一個版本中的pepi)作爲物理地址。如果我現在是這樣,那麼這實際上是虛擬地址。

回答

5

Mmap是通常適用於虛擬地址的功能。當你撥打mmap(... MAP_ANONYMOUS)(或mmap/dev/zero文件)它會給你一些新的虛擬內存,填充零。返回的地址將是虛擬內存的地址。

您可以將某個文件(無MAP_ANONYMOUS)映射到mmap,然後mmap將映射文件內容到某個虛擬內存區域。

該裝置位於地址0x40400000

設備MMIO位於物理存儲器;任何進程都可以使用虛擬地址0x40400000;但是它們會被MMU(內存管理單元)映射(翻譯)到某個空閒的物理頁面。你不能只問一些虛擬內存的操作系統,並期望這將被縮小到設備範圍(這將是地獄的變種)。

但是有一個特殊的設備/ dev/mem,它可以用作包含所有物理內存的文件。 當你的mmap s/dev/mem時,你實際上是要求操作系統創建一些虛擬內存到被問到的物理範圍的新映射。

在你的mmap調用:

mapped_base = mmap(0, MAP_SIZE, PROT_READ|PROT_WRITE, 
    MAP_SHARED, memfd, dev_base & ~MAP_MASK); 

你問映射物理存儲器範圍[0x40400000 .. 0x4050000-1](一兆字節;不包括字節0x40500000)到虛擬內存(其起始地址是由MMAP返回)的一些兆字節。